For reference I am mostly imitating the architecture in this tutorial, the "Entity System" section: http://www.raywenderlich.com/24878/introduction-to-component-based-architecture-in-games
NOTE: the tutorial is in objective-c but I am using C++. Also, I only code the "happy case" for now because I am not sure if this is the architecture I am going with; I am simply experimenting.
Quick background: every time a system's update() function is called, the relevant system calls a helper method in EntityManager, getEntitiesWithProperty(), that returns a vector of all entities which possess a component(s) type that the system operates on.
template <class T>
std::vector<unsigned int> getEntitiesWithProperty()
{
std::vector<unsigned int> ret;
auto key = std::type_index(typeid(T));
std::unordered_map<unsigned int, PProperty*> map = data[key];
for(auto i = map.begin(); i != map.end(); i++)
{
ret.push_back(i->first);
}
return ret;
}
The system then iterates through this vector, calling yet another helper method in EntityManager per each element in the entity vector. This method returns a pointer to the component that is associated with the particular entity that is being iterated over, and the system performs its logic on the entity:
void SRender::update(EntityManager& entM, float frameTime)
{
auto ids = entM.getEntitiesWithProperty<Image>();
for(auto i = ids.begin(); i != ids.end(); i++)
{
Image* img = entM.getProperty<Image>(*i);
draw(img);
}
}
After I implemented this ECS I "stress-tested" it by simply drawing 300 sprites at differing x and y values, and was only able to get around 80 average fps doing absolutely nothing besides rendering.
I then altered the SRender system class and update method so that, instead of calling getEnt..() every frame, it only calls it upon system initialization and every time the EntityManager's component data structure changes size (i.e. entity deleted or created), and stores a vector of relevant components locally within the system as a member variable, like so:
class SRender : public SSystem
{
private:
...
std::vector<Image*> components
}
Without update() calling the helper function every frame, rendering the same 300 sprites every frame I get an increase of 150 average fps. 1k sprites yield 100 fps; 20 fps faster the prior implementation.
My problem with this new approach is that there is no longer a separation of data from function, and that there are more pointers to deal with spread over systems instead of all being contained within the EntityManager, and it overall is just messier to deal with and more bug prone. But I don't know how else to increase performance. NOTE THAT there is nothing flawed from a performance standpoint regarding my rendering pipeline; I did the same initial test WITHOUT actually rendering anything, but simply iterating over each element returned by the helper method, and the fps was exactly the same (around 80 fps average) as it was making draw calls.
What other approaches can I take to improve performance? I have seen other ECS approaches doing the same thing as in the tutorial (returning relevant data structures from the entity manager on every update() call), so I don't understand why my implementation of it has poor performance. Maybe I am overlooking something, or misunderstanding the tutorial (I'm unfamiliar with objective-c)?
std::unordered_map<std::type_index, std::unordered_map<unsigned int, void*>>
and using the getter function could look likeEntities->Get<Image>()
. If you like this approach, I can provide more information. \$\endgroup\$