Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Entity component system: data locality vs. templates

Started by jmakitalo Nov 4, 2014 at 10:41 AM 20 replies 15.2k views
Original Post
jmakitalo
jmakitalo

I'm trying to improve my game/engine design by the use of some clearly defined structuring of objects. My main project is an open world FPS with a lot of static objects, such as trees, rocks etc. In the meanwhile, I figured I could test some new programming ideas on a simple separate platformer game, trying to keep in mind how the ideas would work in the main project.

The ECS seems to be quite popular nowdays, but after the praising introductory, many issues seem to arise.

I have read

http://www.gamedev.net/page/resources/_/technical/game-programming/understanding-component-entity-systems-r3013

http://gameprogrammingpatterns.com/component.html

which give good, but simplified descriptions. I don't want to over-engineer my approach, but I don't want to end up writing a lot of repeated code either.

At the moment, I'm struggling with mainly two things:

1) Data locality: store components in contiguous arrays, so that entities only store indices to these arrays. Alternatively, use base component class and templates and store all component base object pointers in a single array.

2) Implementation of systems: write directly as methods to the class containing the components so that systems can have access to basicaly everything. Alternatively, implement systems as classes and store system base object pointers in some array.

At this point I add some simplified code that hopefully utilizes data locality:


// Components.

struct CTransform
{
 vec3 pos;
 float scale;
};

struct CBoundingShape
{
 vec3 boxSize;
 float radius;
};

struct CMesh
{
 // Refers to some array where mesh data is stored.
 int meshDataIndex;
};

// End of components.

enum EComponentType{
 compTransform,
 compBoundingShape,
 compMesh,
 numComp
};

struct CEntity
{
 // Maps component type to index to component array.
 // Index -1: entity doesn't have the component.
 int componentIndices[numComp];
 
 // For simple entity property tagging.
 long int flags;
};

class CEngine
{
 // Store components in contiguous arrays.
 vector<CTransform> transforms;
 vector<CBoundingShape> boundingShapes;
 vector<CMesh> meshes;
 
 vector<CEntity> entities;
 
 // Add indices to entities here. Only requires transform and bounding shape info.
 CQuadtree *quadtree;

 // Component "getters". 
 CTransform *getTransform(int entityIndex);
 // for each component type.
 
 // Create component and return index to vector.
 int createTransform();
 // for each component type.
 
 void drawMeshes()
 {
  vector<int> indicesToVisible = quadtree->frustumCull();
  
  // Render visible entities that have transform and mesh components.
  // These components are not necessarily contiguous in memory...
  for(size_t i=0; i<indicesToVisible.size(); i++){
   const CEntity &ent = entities[indicesToVisible[i]];
   
   if(!getTransform(ent) || !getMesh(ent))
    continue;
   
   drawMesh(getTransform(ent), getMesh(ent));
  }
 }
};

There are several issues here.

a) Adding new type of components is a hassle and prone to error: for each new type, one has to implement createSomeComponent and getSomeComponent.

b) For drawing meshes, any realistic game will have some spatial partitioning. In my usual implementation, the quadtree/some other returns a vector of indices to visible entities. When using these indices, the entities and their components that are actually processed may not be contiguous anymore.

c) Here drawMeshes() can be seen as a system operating on certain components. By keeping up this way, the CEngine class will soon be bloated with system methods. However, drawing meshes needs access to a variety of the engine's internals, such as textureManager, shaderManager, vertexArrayManager etc. (not shown above). Thus separating systems into individual class objects could still lead to these objects being very tightly coupled with the engine class.

I guess one thing that affects whether (a) will be an issue, is the granularity of components. But I think that in order to take advantage of ECS, one should give a component just one type of thing to store.

I'm thinking if I should have more than one type of ECS. The engine could implement something similar to the above, trying to achieve data locality with the expense of more difficult component management. The game could implement its own ECS, which would be based on template based design, allowing the creation of game specific components easily (here performance might not be so critical). The motivation is that I would think that usually a game world may consist of thousands of entities, but only few of them would be equipped with actual game related components, whereas most would have a transform and a mesh component.

Krypt0n
Krypt0n
a)
if it's just a linear container, you can use a generic type. if the component needs a special structure, then you need to implement accessors to it anyway.

the beautiful part of it is, that you can replace implementations for parts of the components and it all still works. e.g. you can skip registration of all rendering related component managers for a dedicated server and the rest still works and does what it should. you can maintain two ways of managing the same component type i.e. you might have a PhysX and an Havok integration of physics and use them depending on what platform you're running on....

b) I think that might clear it up for you better than my words:
http://dice.se/publications/culling-the-battlefield-data-oriented-design-in-practice/

c) I guess you're sticking too much to the examples. Your CEngine should not be bloated with component access functions, that explicit way would work against the flexibility of the actual idea of an ECS. E.g. creating an object should be wrapped in a factory, CEngine should have a container with all component managers and on creation, you pass the data to the factories and those create the objects. In a super generic way (that's just an example, don't implement it strictly exactly that way tongue.png)

for_all(object in level)
{
  TiXmlDocument Doc(object->Filename());
  UniqueID = GenerateUniqueID();
  m_Objects.push_back(UniqueID);
  for_all(factory in FactoryContainer)
    factory->Instance(UniqueID,Doc);
}
the same way you could process your data

CEngine::Update()
{
  for_all(factory in FactoryContainer)
    factory->Process();
}
and serialize

CEngine::Serialize(Stream& rStream)
{
  for_all(factory in FactoryContainer)
    factory->Serialize(rStream);
}
once you've setup the CEngine that way, you'll probably never have to touch it to add/remove/change a component manager.

and with xml, your object description can but does not have to include a description of the object for a certain component manager e.g. if you create a "player respones component" that will give the entities the property on reacting to the player, 99% of the world might not need that and the manager will not find any description in the xml and either ignore those objects or e.g. create a dummy sound+particle effect if the player shoots at this object. if your level designer decide they want to customize some components, those XML will include the effects. and if you decide your game should now run on the lowest spec hardware and you want to save time processing this component manager, you just don't register it on start up and all the xml setup code will be ignored/dummy.


don't be too focused on data locality and optimizations. your code should not be terrible (performance wise), but first and foremost it should allow you to work effectively. A bad running game is still better than no game with a lot of potential ;)
the ECS will allow you to improve components later on as needed (or even re-implement complete systems), if performance is really an issue.
jmakitalo
jmakitalo




a)
if it's just a linear container, you can use a generic type. if the component needs a special structure, then you need to implement accessors to it anyway.

the beautiful part of it is, that you can replace implementations for parts of the components and it all still works. e.g. you can skip registration of all rendering related component managers for a dedicated server and the rest still works and does what it should. you can maintain two ways of managing the same component type i.e. you might have a PhysX and an Havok integration of physics and use them depending on what platform you're running on....

To use a generic type, I would need to use a single array containing all components. In my sample code, each component was in a separate array and then the creator function needs to know to which array a component is appended. It appears to me that generic types cannot do such array selection.




b) I think that might clear it up for you better than my words:
http://dice.se/publications/culling-the-battlefield-data-oriented-design-in-practice/

I will definitely take a look at this. However, I think Dice had different goals than I do. To me, destructive environment is not that important, nor is console support.




c) I guess you're sticking too much to the examples. Your CEngine should not be bloated with component access functions, that explicit way would work against the flexibility of the actual idea of an ECS. E.g. creating an object should be wrapped in a factory, CEngine should have a container with all component managers and on creation, you pass the data to the factories and those create the objects.

I'm not entirely sure what you mean by factory. Is it a class that contains the components and component management methods? Like "world" as some seem to use? Would you then have multiple worlds in the engine? I was considering that some entities are part of the game map and some are dynamic game objects (e.g. muzzle flash). The latter would not be saved to a file. I guess that you suggested having separate factories for such objects? I was thinking about using the entity flags to mark "map objects". On the other hand, I may want to order the world objects into streamable blocks, so supporting multiple factories/worlds might be a good idea from this point of view.




don't be too focused on data locality and optimizations. your code should not be terrible (performance wise), but first and foremost it should allow you to work effectively. A bad running game is still better than no game with a lot of potential ;)
the ECS will allow you to improve components later on as needed (or even re-implement complete systems), if performance is really an issue.

I'm afraid that the ECS will be so tightly bound to the engine/game code that changing it will prove difficult at a later point.

I came up with one C++ implementation of ECS: https://github.com/TheComet93/ontology

It seems quite nice, except communication between systems seems to be on the todo list.

Another is https://github.com/alecthomas/entityx

which seems to require g++ 4.7 for C++11. I'm not sure if I should write my projects to require this.

Yet another: https://github.com/miguelmartin75/anax

which requires C++11 and some boost dependency.

TheComet
TheComet

communication between systems seems to be on the todo list.


It's already in there, I'm just not sure yet of how well it scales. Basically, I went for the observer pattern approach, so say you have two systems CollisionSystem and SoundSystem, and CollisionSystem would like to tell SoundSystem that it has detected two metal objects colliding (so it can play a sound). To do this you'd create a listener class for CollisionSystem:
struct CollisionSystemListener
{
    virtual void onMaterialHit(std::string materialType) = 0;
    /* other events here */
};
Then in your CollisionSystem class you define a public data member, I'll call it event here:
#include <ontology/ListenerDispatcher.hpp>
#include <ontology/System.hpp>
#include "CollisionSystemListener.hpp"

class CollisionSystem : public Ontology::System
{
public:
    ListenerDispatcher<CollisionSystemListener> event;
};
In order for SoundSystem to receive events, it must inherit the listener interface of CollisionSystem:
#include <ontology/System.hpp>
#include "CollisionSystemListener.hpp"

class SoundSystem :
    public Ontology::System,
    public CollisionSystemListener
{
    // override any interesting collision system events...
    void onMaterialHit(std::string materialType) override
    {
        if(materialType == "metal")
            this->playSound("metalHit.wav");
        /* further types of material hits... */
    }
};
And lastly, you need to register SoundSystem with CollisionSystem so it can receive those events:
SoundSystem& soundSystem = world.getSystemManager().addSystem<SoundSystem>();
CollisionSystem& collisionSystem = world.getSystemManager().addSystem<CollisionSystem>();

collisionSystem.event.addListener(&soundSystem, "sound system");
Now, inside your CollisionSystem collision check routine, if you detect two metal objects colliding, you can call:
this->event.dispatch(&CollisionSystemListener::onMaterialHit, "metal");
"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
jmakitalo
jmakitalo




It's already in there, I'm just not sure yet of how well it scales.

Thanks for the update. Can you see any big differences between your implementation and entityx ( https://github.com/alecthomas/entityx )?

One thing a quick look suggests is that the systems in ontology have processEntity() that processes a single entity, whereas

entityx has update() that passes the entity manager, i.e., all entities can be handled within this one method call. For many entities,

the latter might be better for performance (also considering how things are rendered in modern 3D APIs).

I could try to learn by rolling my own ECS, but as there seem to be existing light-weight solutions, it would make sense to make use of them.

Thus the best approach for me might be to try to study these existing codes.

TheComet
TheComet

A quick comparison reveals some differences.

Ontology does make an effort to store entities in contiguous memory, but it doesn't store components in contiguous memory. I've done a lot of stress tests with ontology and in the grand scheme of things the increased amount of cache misses are irrelevant, but it's worth mentioning. EntityX stores everything in contiguous memory, but at the price of having to look the entity/component up via a unique ID. I'm not entirely sure what's more inefficient - cache misses or having to lookup O(log(n)) every loop for every component.

I couldn't find a way to support polymorphic systems in EntityX, where ontology does have a method addPolymorphicSystem(). This is useful for abstracting the exact implementation of a system.

Ontology has support for declaring the components a system supports statically, vs EntityX doing it dynamically. There's no real upside/downside to these implementations, it's a matter of taste. Ontology will throw an error if you try to access a component that isn't supported, EntityX will silently ignore it.

Ontology has support for creating an execution dependency graph of systems, so you have a lot of control with re-ordering when which system is updated, independent of the order in which you add the systems to the world.

Regarding the update method: In the end it really boils down to the same thing. Entityx gives you the list of all entities, and it's up to you - the system implementer - to filter through the entities that you support and process them:


// EntityX' implementation
struct MovementSystem : public System<MovementSystem> {
  void update(entityx::EntityManager &es, entityx::EventManager &events, TimeDelta dt) override {
    Position::Handle position;
    Direction::Handle direction;
    for (Entity entity : es.entities_with_components(position, direction)) {
      position->x += direction->x * dt;
      position->y += direction->y * dt;
    }
  };
};

As you can see, every time you update a system, you need to filter the entities you support with: for (Entity entity : es.entities_with_components(position, direction))

I didn't really see any use for having access to entities you cannot support, which is why my implementation pre-processes the entities and every system holds a list of references internally to the entities it supports. My implementation hides all of the filtering and iterating so you can focus on processing the entity.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
jmakitalo
jmakitalo




A quick comparison reveals some differences.

Ontology does make an effort to store entities in contiguous memory, but it doesn't store components in contiguous memory. I've done a lot of stress tests with ontology and in the grand scheme of things the increased amount of cache misses are irrelevant, but it's worth mentioning. EntityX stores everything in contiguous memory, but at the price of having to look the entity/component up via a unique ID. I'm not entirely sure what's more inefficient - cache misses or having to lookup O(log(n)) every loop for every component.

Thanks a lot for your effort! Yeah, I'm not sure how big an issue cache misses will turn out. If processing thousands of entities, the issue may surface if everything is otherwise done properly. I would guess that looking up components from a linear contiguous array can be pretty fast, but this would just have to be tested.




I couldn't find a way to support polymorphic systems in EntityX, where ontology does have a method addPolymorphicSystem(). This is useful for abstracting the exact implementation of a system.

I'm not sure where this would become handy. Maybe if you make some basic AI system, then you can make some other AI system based on the first one, but overriding certain behaviour? Then you would end up with kind of mix of component and object-oriented design.




Ontology has support for declaring the components a system supports statically, vs EntityX doing it dynamically. There's no real upside/downside to these implementations, it's a matter of taste. Ontology will throw an error if you try to access a component that isn't supported, EntityX will silently ignore it.

That's interesting. Ontology's approach does sound more secure, but may potentially be inflexible.




Ontology has support for creating an execution dependency graph of systems, so you have a lot of control with re-ordering when which system is updated, independent of the order in which you add the systems to the world.

This may be important, but I have to carefully think how I would execute systems in my game. It's certainly possible that wrong ordering will produce nasty bugs.




Regarding the update method: In the end it really boils down to the same thing. Entityx gives you the list of all entities, and it's up to you - the system implementer - to filter through the entities that you support and process them:

I don't really agree with this. If you are making a game with OpenGL/Direct3D, the rendering system will want all renderable entities at the same time. You go through the entities and build a list of draw batches from them. then sort the list by render states (shader, texture, VAO) and then render the sorted list. If the render system gets one entity at a time, this is not possible, at least directly. Also, if you have thousands of objects, there will be a lot of methods calls. I think it was already in http://www.gamedev.net/page/resources/_/technical/game-programming/implementing-component-entity-systems-r3382 that the initial approach was to loop through entities and call systems, but it was changed into looping through systems first and examining the list of entities within each system. I would guess that usually there are a lot more entities than there are systems.

Regarding both ontology and entityx, I'm not still sure how to hook up a spatial partitioning with a rendering system. In my original simple example, I had a simple array of entities and the quadree could then just return an array of indices to visible entities. I wonder if this can be applied directly with ontology or entityx. At least the entities would have to be located at the same place in an array even if some entities are added or removed, so that the entity indices stored by quadtree remain valid. Then the question is how to pass the entities, that have been deemed visible, to the rendering system? The set of entities would have to be the intersection of "renderable" entities (proper set of components) and visible entities.

TheComet
TheComet
I'm not sure where this would become handy. Maybe if you make some basic AI system, then you can make some other AI system based on the first one, but overriding certain behaviour? Then you would end up with kind of mix of component and object-oriented design.

Basically, yes. Another example from my game would be an input system:


m_World.getSystemManager().addPolymorphicSystem<InputInterface, SDLInput>();

You can dynamically (or statically) switch in and out various input managers (I have another IOSInput class, for instance) without having to change any other code in the process. The other systems will still be able to call getSystem() without knowing about the implementation change.

I don't really agree with this. If you are making a game with OpenGL/Direct3D, the rendering system will want all renderable entities at the same time. You go through the entities and build a list of draw batches from them. then sort the list by render states (shader, texture, VAO) and then render the sorted list. If the render system gets one entity at a time, this is not possible, at least directly. Also, if you have thousands of objects, there will be a lot of methods calls.

That's a good point.

I think I'll add that functionality to Ontology, thanks for pointing that out.

Another approach: Wouldn't it be possible to split the various stages up into systems? I.e.

  • BuildDrawBatchSystem would group the renderable entities together into various groups
  • SortRenderStatesSystem would sort the lists into render states
  • RenderSystem would finally render everything

You could pass the lists around via an entity or just communicate it directly between systems.

The downside of course is the function call overhead of processEntity().

I wrote Ontology mainly as a learning experience and with a naive approach. In my experience, it's not that hard to do, so maybe you'd want to write a specialised ECS for your render system? That way you can tailor it to your exact needs.

"I would try to find halo source code by bungie best fps engine ever created, u see why call of duty loses speed due to its detail." -- GettingNifty
jmakitalo
jmakitalo




Another approach: Wouldn't it be possible to split the various stages up into systems? I.e.

BuildDrawBatchSystem would group the renderable entities together into various groups
SortRenderStatesSystem would sort the lists into render states
RenderSystem would finally render everything

You could pass the lists around via an entity or just communicate it directly between systems.



The downside of course is the function call overhead of processEntity().

This approach might certainly work, but the function call overhead worries me.

Repeating one of my earlier remarks: how would you apply frustum culling by some spatial partitioning system (e.g. quadtree)? The renderer only want's visible renderable entities, usually denoted by a list of indices from the visibility system. A naive approach might be to add/remove visible-components to entities after frustum culling, but I bet this will be very inefficient.




I wrote Ontology mainly as a learning experience and with a naive approach. In my experience, it's not that hard to do, so maybe you'd want to write a specialised ECS for your render system? That way you can tailor it to your exact needs.

I'm most often tempted to roll my own things, but have sadly noticed that they turn out less than great :D. I'm intrigued in writing the engine and game very tightly around the ECS with messaging and having an existing rigid ECS framework could streamline the process.

MakaiKingCross
MakaiKingCross

Just a little interjection:

I've been studying ECS models for over a year now and I can tell you they're not a solve all solution. There are quite a few issues that you should think about.

First Of All: Though the concept is good and I do agree it's a step in the right direction, there are too many different models and most of them don't really look at the issues involved in using this model.

ECS is really a model that should only be used if:

1) You want to simplify adding/removing of entity types for others.

2) OOP is getting to bulky

3) You want to move your code to a newer more viable option than OOP and are just starting to improve and optimize your coding style.

Second Of All: The majority of and most commonly used approach to an ECS, is to store your entities and various components in Vectors of Unique Pointers. This sounds like a great idea.... ITS NOT! Here's why, Memory Access Efficiency....

When your program pulls information from memory, it does so in batches, to optimize efficiency. Array types like Vectors are designed around this concept by forcing all of it's contents to exist in the same segment of memory such that when it is pulled, the entire array type is pulled, why? Because typically if your pulling a piece of data then your most likely going to access something near it as well. If you create a container of pointers which point to various locations in memory you napalm this efficiency, because the array type only ensures that the pointers are all in one location, not the corresponding entities and their components, so your essentially making a lot of slow calls to various locations in memory for any single object in your game.

Third Of All: The ultimate point of an ECS is to eliminate connectivity between every aspect of your code. Again, not a bad idea... just that it's generally poorly implemented. So you have to setup some sort of complex communication between not only your systems, but between your entities and components.

In C# this is typically done using events, but as any C# programmer can tell you, this is a sucky solution! In C++ you have a lot more control over the code and how it works so you could probably come up with a much more efficient solution than Eents, like maybe a work queue. But the point is, regardless, your going to have a crap ton of messages flying all over the place which can make things kind of tricky and messy.

I'm not saying don't use an ECS Model though, simply be careful how you use it and don't fall into the coding pitfalls that it presents. I myself am designing a new type of ES model that's based on concepts from various programming patterns, mainly the ECS model.

Just thought I'd add in a few snippets for though... Good luck! smile.png

mark ds
mark ds

A lot has been said about cache misses, which is valid to a degree, but don't forget that intrinsics exist which at least help with the problem, namely _mm_prefetch, and derivatives.

This link is a little old, but it's still valid today: Prefetch

jmakitalo
jmakitalo




3) You want to move your code to a newer more viable option than OOP and are just starting to improve and optimize your coding style.

Isn't code improvement something one should always pursue? I think that ECS is also something that you should decide before starting to write a game or engine, as it will be used by so many objects. Later on (my case, sadly) it may be difficult to substitute some excisting OOP scheme with ECS or any other for that matter.




When your program pulls information from memory, it does so in batches, to optimize efficiency. Array types like Vectors are designed around this concept by forcing all of it's contents to exist in the same segment of memory such that when it is pulled, the entire array type is pulled, why? Because typically if your pulling a piece of data then your most likely going to access something near it as well. If you create a container of pointers which point to various locations in memory you napalm this efficiency, because the array type only ensures that the pointers are all in one location, not the corresponding entities and their components, so your essentially making a lot of slow calls to various locations in memory for any single object in your game.

This is one of the key questions in my topic: how to utilize cache in ECS. However, if you read the previous discussion, some existing solutions were mentioned that do impose data locality.




The ultimate point of an ECS is to eliminate connectivity between every aspect of your code. Again, not a bad idea... just that it's generally poorly implemented. So you have to setup some sort of complex communication between not only your systems, but between your entities and components.

This is true. The existing implementations mentioned previously do implement messaging too. Although I'm not sure what the performance hit will be.




In C# this is typically done using events, but as any C# programmer can tell you, this is a sucky solution! In C++ you have a lot more control over the code and how it works so you could probably come up with a much more efficient solution than Eents, like maybe a work queue. But the point is, regardless, your going to have a crap ton of messages flying all over the place which can make things kind of tricky and messy.

I think that a queue of messages/work is usually the pattern for implementing temporal decoupling, i.e., at some point you know that some work must be done, but not right away. I'm quite sure that this is not what ECS should use. Perhaps you didn't even mean this, but I think it should be mentioned any way. The messages should lead to instant excecution.

A lot has been said about cache misses, which is valid to a degree, but don't forget that intrinsics exist which at least help with the problem, namely _mm_prefetch, and derivatives.

This link is a little old, but it's still valid today: Prefetch

This may well become handy at some point. I'm not familiar with all the instrinsics, although I have used SSE intrinsics for optimizing certain intersection test routines.

One thing that bugs me with avoiding cache misses is that is it really going to work even if the entities and components are stored in contiguous arrays? In a simple game, where e.g. all meshes are visible each frame, surely data locality will pay off. However, if you have a large 3D world with 100k entities, only 1k or so are usually visible at any given time and there is no guarantee that these happen to be contiguously stored in memory even if all the 100k entities are. Say that it happes that every 100th entity is visible one and a cache line only fits less than 100 entities.

There might be some ways to fight this. Perhaps the entities should be arrenged in memory so that spatially close by entities are also close by in memory. For example, if the world is divided into a grid, then each entity in a grid cell would be located contiguously in memory. However, this can still be scewed up if the visibility culling algorithm returns indices to the entities in some mixed way, or if the entities are sorted by distance (for example to prevent overdraw). Another thing is that if an entity has a lot of data, but some of it is not used in hot code, then that portion of data could be referenced by a pointer by the entity to decrease the memory footprint and possibility of cache misses.

jmakitalo
jmakitalo

In my original code example, there were several drawbacks:

  1. Maintain a list of component type identifiers (const integers)
  2. Not really typesafe, entities store component references as integers
  3. Component management was not templated: a lot of repeated code

I think ECS could also be implemented in such a way that an entity is nothing more than a tag and the component managers handle entity-component relations.

This approach pretty much solves the above problems:


class CEntity
{
friend class CWorld;
private:
  // Index of entity in a list of entities. For fast removal only.
  std::size_t index;
  
  // Only world can create entities.
  CEntity(std::size_t _index) : index(_index) {}
};

template<class T>
class CComponentManager
{
private:
  // Contiguous storage of components of type T.
  std::vector<T> components;

  // Maps an entity to an index to components vector.
  std::map<CEntity*,std::size_t> indices;
  
public:
  // Add component to given entity. Some components may require initialization so pass a prototype.
  T *add(CEntity *entity, const T &component)
  {
    components.push_back(component);
    
    indices[entity] = components.size()-1;
    
    return &components.back();
  }
  
  void remove(CEntity *entity)
  {
    std::map<CEntity*,std::size_t>::iterator it = indices.find(entity);
    
    if(!it)
      return;
      
    components[it->second] = components.back();
    components.pop_back();
    
    indices.erase(it);
  }
  
  T *get(CEntity *entity)
  {
    std::map<CEntity*,std::size_t>::iterator it = indices.find(entity);
    
    if(!it)
      return NULL;
      
    return components[it->second];
  }
};

class CWorld
{
private:
  CComponentManager<CTransformationComp> transformations;
  // Add other component managers here.
  
  std::vector<CEntity*> entities;
  
public:
  CEntity *addEntity()
  {
    CEntity *entity = new CEntity(entities.size());
    
    entities.push_back(entity);
    
    return entity;
  }
  
  void removeEntity(CEntity *entity)
  {
    entities.back()->index = entity->index;
    entities[entity->index] = entities.back();
    entities.pop_back();
    delete entity;
  }
  
  CComponentManager<CTransformationComp> &getTransformations()
  {
    return transformations;
  }

  // Getters for other component managers here.
};

CEntity *createMeshEntity(CWorld *pWorld)
{
 CEntity *e = pWorld->createEntity();
 pWorld->getTransformations().add(e);

 // Add other components.

 return e;
}

A separate entity manager could also be made instead of integrating it as part of the world.

One drawback here is that O(logN) search is done when getting components of an entity.

SeanMiddleditch
SeanMiddleditch
Obligatory reading:

http://bitsquid.blogspot.com/2014/08/building-data-oriented-entity-system.html

I don't agree with all of the things the BitSquid guys do, but in general that blog is a pure gold mine. Read it start to finish, and then put it on your list of blogs you follow.

I'm not entirely sure what's more inefficient - cache misses or having to lookup O(log(n)) every loop for every component.


Why is it not an O(1) lookup? It should be an O(1) look up.

Even then, an O(log(N)) lookup via binary search on a contiguous packed range (e.g. a boost::flat_map or equivalent) is really, really fast. It still involves cache misses in the worst case but for frequently-accessed component (transform) it can result in less than you'd think, as well as for components with few actual instances.

The ultimate point of an ECS is to eliminate connectivity between every aspect of your code. Again, not a bad idea...


Not really. That's an aspect of general component-based design. ECS is one very narrow and over-specified approach to component-based design. Not a particularly tenable one, IMO. You can use bits of ECS-like design where it matters the most (transform, scene graphs, physics, etc.) and ignore it where it doesn't and the boilerplate is just a pain in the butt.

A lot has been said about cache misses, which is valid to a degree, but don't forget that intrinsics exist which at least help with the problem, namely _mm_prefetch, and derivatives.

This link is a little old, but it's still valid today: Prefetch


You can't really prefetch when the memory is strewn all over the place and you have to make a memory access just to find out what the next piece of memory you need is (e.g., most trees).

If you're just iterating over a contiguous chunk of memory, the compiler _and_ the CPU both do a pretty good job of figuring out this stuff automatically. Misuse of the intrinsics can hurt, so don't use them without good reason and absolute confidence that they'll help (e.g., profile before and after).
Sean Middleditch – Game Systems Engineer – Join my team!
L. Spiro
L. Spiro

// == GRAPHICS == //
In regards to everything that has been said/suggested about how to render the entities, there are too many quotes for me to pick any specific one and I don’t want people to misunderstand that I am replying to any single quote.

The original concern of the poster was a tight coupling between the renderer and objects.
This should never be the case if you don’t let the renderer know what entities (and also if you stop calling it a renderer) are.

There is no “renderer”. Rendering is a complex process with many stages and systems that need some form of communication. It is high-level and it is not done by only 1 single library. A graphics library is not a rendering library (except in that is does have the functions that actually issue draw calls (DrawIndexedPrimitive(), etc.)).

Just because rendering itself is a high-level process it doesn’t mean the graphics library is a high-level library. It doesn’t know what a model is, or what a scene is, or entities, or even what a mesh is. It knows about the vital low-level parts of each of these things—shaders, textures, vertex buffers, index buffers, matrices, etc.

If I want a model in my game, the model library itself is a completely different library and it is higher-level than the graphics library. It creates textures and vertex buffers which it finds in the graphics library. Communication is one-way—the graphics library doesn’t know where these textures and vertex buffers are going.

Terrain renders in a wildly different manner and is yet another library by itself, so it doesn’t make sense that the graphics library should know about everything that can be drawn. Not everything has to be a new library (you can decide on that granularity by yourself), but they definitely should not be lumped together into a megalithic library with no clear single responsibility. I haven’t even mentioned foliage, clouds, water, etc.


It should be very clear by now that the concept of a renderer makes no sense.
Instead, a few systems need to work together. Each type of renderable object should know by itself how it needs to be rendered. Foliage may use some common resources used also by models, but how they prepare for a render and the steps involved in picking LOD levels, culling, and getting the render achieved are completely separate code paths.

If objects can submit their own final draw calls, next you need a way of picking which objects to draw.
The scene manager is the high-level class that knows how to orchestrate this process. It stores all objects and cameras etc., and it can perform culling and create a list of objects inside the relevant frustum.

Now you have a list of objects to draw and a way to draw them. Next you need to sort them. Sort by priority but also render state for efficiency. This render-queue can come from the graphics library and only needs to do a fast sort based on integers and a float. No exchange of library-specific information.
The scene manager creates as many render-queues as it needs, and things are loosely coupled.

Once things are sorted, you need a way to go back to the original objects in sorted order to let them draw themselves. The objects can implement an interface that allows the render-queue to store a pointer back to the object, at the cost of a single virtual pointer indirection.
The world manager walks the render-queue and calls a virtual function on each object in order which allows each object to draw themselves in proper order without any tight coupling between the objects.

The graphics library hasn’t become tightly coupled to models or terrain, or the game world. All connections between objects are logical.


// == ECS == //
As has been mentioned, ECS might seem popular right now, but that doesn’t mean it is right for you, and the fact is for indies, hobbyists, and small developers it very likely isn’t.

There are 2 things that all major implementations of ECS have accompanying them: Tools and scripting languages.

The primary purpose of ECS is to be able to add components easily and especially to customize objects outside of code. If you don’t have this ability then you have virtually no reason to use ECS (at most you should use a hybrid).

If you have to add properties via code then you would be equally well off (or better off) via static composition or inheritance. You could be better off this way since your code will be much more logically centralized.

If you don’t have scripting then you still have to add game logic entirely via code.


You may not have considered how the tools and scripts in Unity 3D for example augment their ECS implementation, but if think about what you will have to code in order to get the same results but without an editor and scripts you will start to realize that without those things, ECS really makes no sense.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
jmakitalo
jmakitalo




Why is it not an O(1) lookup? It should be an O(1) look up.

If you store e.g. component identifiers in a std::set in an entity, then you need to search the set to find if an entity has a component. Of course there may be ways to circumvent this by some lookup tables, which sacrifices some memory.

It seems quite difficult to get only O(1) lookups, get data locality and avoid using any runtime type stuff or dynamic casts.




The original concern of the poster was a tight coupling between the renderer and objects.

Well... this topic is not specifically on this matter, but more generally on ECS: how to minimize the amount of code required when adding new components and how to impose data locality. Implementation of systems is also one concern and this can relate to the coupling of renderer and objects somewhat.




There is no “renderer”. Rendering is a complex process with many stages and systems that need some form of communication.

I hope I didn't directly suggest this. My engine has managers for materials, meshes, shaders and such resources. It has some spatial partition system that stores indices to renderable objects. These pieces are all well/logically isolated from each other. The render objects hold some references to the resources they require. To render objects, I usually query spatial partition for indices to visible objects. Then build a render list and sort it by resource id:s, distance to camera etc. Then I have a routine that knows how to draw the objects via the render list, given also the required resource managers.




Each type of renderable object should know by itself how it needs to be rendered.

I disagree. I think a renderable should be more like POD, containing references to resources and some transformation. In my engine, I have a method which takes as input a list of renderable objects (or a list of indices to it given by some culling and sorting process). It also takes as input the resource managers. This method knows how to draw the objects efficiently together. The objects individually don't know how to render themselves and don't implement such functionality. I have found this pretty good design.




There are 2 things that all major implementations of ECS have accompanying them: Tools and scripting languages.

The primary purpose of ECS is to be able to add components easily and especially to customize objects outside of code. If you don’t have this ability then you have virtually no reason to use ECS (at most you should use a hybrid).

I didn't mention it, but my engine has integrated editing capabilities. One advantage of ECS is that if I have editing feature for traslating/rotating/scaling objects/entities (meshes, sounds, AI triggers, spline paths, etc.), the editor only requires the transformation components and can work on those, regardless of the type of entity. With static composition, it is difficult to get this type of flexibility. You could use inheritance so that these entities inherit CTransformable (this is what I do at the moment). But then the different entities are stored in separate arrays (as they have different type), so getting all transformables is a hassle.

Another example is that once entities have been constructed, they should be inserted to e.g. quadtree as some references (integer indices, pointers, etc.). The constructor of the quadtree only has to know the transform and bounding box components and in ECS they are nicely obtained regardless of the composition of entities. How would this be conveniently done with static composition?

jmakitalo
jmakitalo




Obligatory reading:

http://bitsquid.blogspot.com/2014/08/building-data-oriented-entity-system.html

Oh, that's a great resource. Thanks!

The text describes an approach with many similarities with one I posted before your post SeanMiddleditch. To handle the "entity-component" relations in the component managers is a great approach, because the entity does not need to store inhomogeneous data, i.e., references to components of different type. This is very difficult to get right in C++ in a typesafe but still performant way (no typeid or dynamic_cast).

The blogpost also discusses the concern I raised that checking if an entity has a component is O(logN) where N is the number of entities. The way of bitsquid for implementing component managers is much more relaxed than what I had figured. Of cource it comes with the cost of having to repeat more code if many component managers store data in the same way.

I think that I could utilize a possibility mentioned by bitsquid that a component manager can decide how entity-component relations are stored. For e.g. transformation components, which almost all entities use, a vector lookup with O(1) complexity would be appropriate. For some more infrequenly occuring components, a map would be better.

The blogpost also discusses a problem that had occurred to me: how to delete the components when an entity is deleted. The garbage collector approach suggested might work. At least it avoids tight coupling between entity and component managers.

SuperG
SuperG
Also reading a lot about ECS. And I find this online book to go very deep into it.

http://www.dataorienteddesign.com/dodmain/node1.html

After reading so much different perspective on ECS.

I got this impression.

ECS is not a solution. But more a group of solution it looks more a name for a specific problem domain.

Depending on the kindof game you got a specific problem to solve with ECS so there might be right and wrong soltutions for a specific game but if they are wrong or right for other game might differ.

First step is going the OOP way to avoid inherritance and go for composition or aggegration.
Then up to a OOP And DOD hybrid
To pure DOD.

I also see some resistance to DOD or ECS. It might be because the veteran experienced programmers are indoctrinater or very used to the OOP kind of thinking.

As a novice programmer I don't have this legacy so OOP and DOD I have not much experience with.
But as a novice I want to learn programming the right way and future proof. And DOD seams to have the more and best argument to go for.

OOP would be nice to prototype something very basic and small. As it modeled to real things.
DOD is more taking the perspective of the hardware platform how it execute your code and data.

My back ground is electronica and with limited experience with Z80 and 6800 assembler.
So the hardware perspective isn't that weird for me.

But to get the most out of DOD you need to know the platform you program for. Where OOP ignors that.

Also I read a lot about how bad multitreaded games are. And DOD makes concurrent computing much easier to implement.

As novice I would start small where ECS and DOD and Concurent computing are not that relevant. And are heavy weight solution for something very basic. But I want to scale up pretty fast.
Because as hobby programmer I want to make the games I also want to play. And I am not into retro gaming. And not into the small indie projects. The games I play are those triple A or Retail games.

The kind of game Which is my favorite are the space sim sandbox games.
There are two big independed games made by professional studio.
Elite dangerous and Star Citezen.

This genre has it specific group of ECS requierments.

Like could. Entity be a component to.
Thinking about docking.
Have more then one of the same component. Many guns turrets thrusters.

This genre suits DOD very well. You got many different type of things and of a lot of them you get often many.

Currently just hacking a dx11 tutorial or example. Got position array in it. It a start.
Also get instance rendering up and running.
haegarr
haegarr
OOP would be nice to prototype something very basic and small. [...]

It is not the paradigm in itself that allows for convenient prototyping; its the programming language and the library and perhaps even tool support.

[...] As it modeled to real things.

Notice that "real things" is also too restrictive. You can of course also model non-things like, say, a faction, emotions, behavior, a plan to follow, and so on.

However, Let's come to the main reason for my post:

This genre has it specific group of ECS requierments.

Like could. Entity be a component to.
Thinking about docking.
Have more then one of the same component. Many guns turrets thrusters.

For sure, specific genres often have their specific types of components. But besides that, what architectural requirements are introduced by genre?

"Entity be a component to" is IMHO a wrong approach per se, because an entity would no longer be just an ID or at most a container of components. What does it mean if you drop an entity into another? Both have a placement, a mesh, a material. How are they related, and how are they delimited? This obviously leads to a similar problem than traditional scene graphs have. Moreover, is an entity that represents a knife really a component just because an entity representing a person can hold it in its hand? Instead, expressing the relations between entities by using explicit components fits the paradigm much better.

Let's have a look at the ParentingComponent as a kind of spatial relation. Each entity has a PlacementComponent which stores the position and orientation of the entity in the world. If a ParentingComponent is attached to an entity, it means "this entity is related to another one so that forward kinematics is applied to this entity". To be able to do this, the ParentingComponent stores a local transform as well as a link to the parent entity.

Another example, going a bit towards the gun turrets, is a 4 wheel vehicle. As such it contains of a body and 4 wheels. Let's say the wheels should be entities. A solution would be to add a Chassis4WheelComponent instance to the vehicle's entity. Such a component provides 4 slots to link other entities, their local placement and perhaps even their rotational behavior in dependence on motion.

Notice please that such an approach also give a simple way for equipping.

Let's go a bit further: What if an entity is related to 2 other entities? 2 entities representing persons are playing tug of war with an entity representing the rope. In this situation it would even not be clear how the rope as an "entity is a component" would be assigned. But if only the relation between all 3 would be expressed, all is fine.

Of course, these are my 2 cents. Until now it works for me. Looking forward, I'm interested in situations where another solution would be needed...

TheChubu
TheChubu
If you store e.g. component identifiers in a std::set in an entity, then you need to search the set to find if an entity has a component. Of course there may be ways to circumvent this by some lookup tables, which sacrifices some memory.

The way I deal with this in dustArtemis is much like Artemis, each entity has a bit set, each component type has an index, you check if an entity has a component by testing:


entityComponentBits.get(componentTypeIndex) != 0

Then components are essentially stored as arrays of pointers (using Java here), so to get the component of an entity you do:


Component[] cmpArray = componentsByType[componentTypeIndex];
Component c = cmpArray[entityID];

Its fairly direct, one pointer indirection to fetch the array, another indirection to fetch teh component. You most probably can trim it down the indirections in C++. Although I'd actually select different storing strategies for component arrays. Some of them will be worth to be stored in arrays of pointers, some of them you might want stored contiguously by value in entity -> component maps. That depends on the actual game usage patterns.

"I AM ZE EMPRAH OPENGL 3.3 THE CORE, I DEMAND FROM THEE ZE SHADERZ AND MATRIXEZ"   My journals: dustArtemis ECS framework and 

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.