Entity pooling and EntityObservers

Published October 19, 2014
Advertisement
In this update: Entity pooling and refactoring EntityObservers.

[font=arial]

dustArtemis

[/font]


[font=arial]dustArtemis is a fork of Artemis Entity System, which is a BSD-licenced small Java framework for setting up Entities, Components and Systems.[/font]

[font=arial]Entity pooling[/font]



Well, in an ECS framework, first thing comes to mind when dealing with pools is pooling the entities.

One of the changes in dustArtemis compared to original Artemis is the IdAllocator, this IdAllocator is sort of a free list memory allocator but in charge of ID numbers for entities. It keeps track of the range of IDs (in the positive 'int' space by default) that can be assigned to entities, and each time you try to "allocate" an ID, it will return the lowest free ID available.

This is very important since IDs are used for indexing into component arrays, while BoundedBag gets rid of some of the hassles, if the provided IDs are disperse, the Component bags will be too.

EntityManager is in charge of creating new Entity instances, it allocates a new ID from its IdAllocator and returns a new Entity instance. When Entities are deleted, it frees their IDs.

The new PooledEntityManager now pools Entity instances, for this Entity id had to be made mutable inside the framework, so the manager could assign it the lowest ID number possible and return it as if it were a new Entity.

Now, this is configurable thanks to two new constants in DAConstants: POOL_ENTITIES, a boolean which indicates if Entity instances are to be pooled or not, and MAX_POOLED_ENTITIES, an int value that indicates the max amount of entities that will be held in the entity pool.

If you set the limit to, say, 2000 entities, the manager won't store more than 2000 entities that are deleted. If you delete more than that, it will discard them.

By default its set to Integer.MAX_VALUE, ie, without effective limits. This means that the pool will peak at more or less the amount of entities you have alive in the entire time its running. This value might be okay with you, or it might be not, thus why you can configure it if you want.

But you shouldn't pool objects!



It depends on your requirements. In a videogame where a few millseconds of GC is the most annoying thing ever, yes, you do. This is not because Entity objects are particularly heavy, this is for reducing GC pauses.

For example, in my system, there is no apparent difference in performance when pooling entities vs not pooling them at all. Allocation is fast in the JVM, really fast. The issue is when those entities are being collected. If you go crazy, ie, each bullet is an entity, each particle is an entity, you'll have lots of GC pauses. Thus why pooling might be necessary.

Now, this is a double-edged sword. Pooling objects mean they'll get promoted to "old generation", since they're long lived. This is an issue because GC runs in "eden space" are quite fast. "old generation" GC pauses are longer. Thus you'll be increasing the work the JVM has to do in the "old generation" space.

As with everything: Profile, VisualVM is your friend.

Managers and Systems



This is something I thought it was kinda silly. EntityObservers define the various "events" all systems/managers can respond to: To added entities, removed entities, changed entities, etc.

From here two "branches" sprout, the ones in the form of EntitySystems, and the ones in the form of Managers. So far so good.

Thing is, the only difference between EntitySystem and Manager class was that EntitySystems get "processed" and can be "active" or not. As for the rest, both implemented EntityObserver, and both defined their own World field. Another difference is that World processed managers first, then entity systems. Also EntityManager was a Manager, thus an EntityObserver, so it was notified when entities were added/deleted.

So I just moved up the responsibilities! Now all EntityObservers hold a World instance, all EntityObservers can be processed, initialized and disposed of. EntityManager gets initialized in the World constructor and its added first in the chain of the observer update list.

This removes the necessity for two separate HashMaps/Bags of "systems" and "managers" in the World instance. Now you just add observers to the world, regardless if they are "managers" or "systems", essentially unifying the way World operates with them and simplifying the code.

Now, the future!



I still have more things in mind: First, Component pooling. Now this one is much more trickier, Components are defined by the user, thus they can be anything, from GPU buffers to sound files, or just simple 3 float array for positions. Some of these make sense to be able to pool, some of these don't. The user needs to be able to configure that. Which components get pooled and how.

Also there is the other thing I have on the "Issues" page in the repo, Maven integration. Not that I use Maven, but it will be necessary for integrating dustArtemis with junkdog's entity system benchmarks. Plenty of people use Maven so I suppose it will be a good learning experience.

Thats it for this entry, cya later.
2 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement