Game Development Community

Entity/Component System R&D Discussion

by Jeff Raab · in Torque 3D Professional · 07/24/2014 (1:56 pm) · 105 replies

Continued from here: www.garagegames.com/community/blog/view/22794

Per Dan's suggestion, lets move the hardy discussion and specifics into a thread, where the back and forth is more practical

Quote:
Is there a better place we should discuss this? I was thinking... why are interfaces separate from components? AFAICT, the purpose of an interface is to say 'find me a component on which I can call "getVelocity"'. Could components not do that themselves? Since the whole system relies on dynamic_cast anyway, that is. I guess this would look like components inheriting particular interfaces.

The problem with calling anything directly is for it to correctly type, we'd have to know the specific component's class.

So lets say we want to have a few different colliaion components, to handle various circumstances:

BoxCollider, CapsuleCollider, SphereCollider, MeshCollider.

They have the same basic getCollisionInfo(), hasCollided(), etc calls, but the work they do in them is particular to themselves, right?

So if you wanted your Physics component to correctly call the component's function directly, there's 2 ways to do it.

Either we have an intermediate class 'CollisionComponent' all physics components inherit from, which bulks the heirarchy while still having us use virtual functions anyways, or the Physics component just knowing about every type of collision component we have and checking against them all.

So we have our physics component would have to figure out what component it is to correctly call the function in question. It also means we're adding to the number of headers we have to compile in, increasing compile times. If you wanted to add a new collision component, you'd have to add in the header and typing stuff for the new collider as well.

Then lets say we need to have more than one kind of physics component, such as SimplePhysics, RigidBodyPhysics, SoftBodyPhysics, etc, etc.
We multiply the above considerations for each.

Pretty fast you're going to end up with a ton of excess cross-references just so you can call the functions directly. It'd raise compile times and make adding new components kind of a pain.

With the interfaces as-is, it may be kinda ugly, but none of the above are problems.
You standardize the interface and it's functions, and those virtualize to the component's own interpretation of the interface calls, and does it's work.

This way, the components don't at all care what the other components are, and the same for Entity-Component interactions.

The code may be ugly, but the usage is a lot cleaner and robust.

Unless there's away around the nexus-of-pain of cross-references mentioned above?
#61
01/02/2015 (9:40 am)
@azaezel: On further inspection I guess you're right, I was looking at Jeff's SimplePhysicsBehavior which handles moving the object internally, but now that I look closer at the regular PhysicsBehavior it seems to call out to a plugin (PHYSICSMGR) as you point out.

I guess my question then is would it be worthwhile to dig into the existing physics plugins and back them away from GameBase in favor of entity. My biggest interest in this whole thread is the potential of eliminating the bloated Torque hierarchy and GameBase is closer to the bloat end of the pool than I'd like to be. (I'd rather not have the ability to eject shell casings from the belly of my dragon, lol.)

I'm looking for as lean an object as possible that can still render itself, play animations, and have physics done to it. At least I think that's what I want, I could still be wildly confused. :-)

#62
01/02/2015 (9:46 am)
@Chris
Oh man, my whole goal is to eventually murder GameBase(and probably conver SceneObject into a component and have Entities take it's place too).

But yeah, whole idea is to get rid of AT MINIMUM gamebase-and-down.

Also, check my prior reply about the physics component stuff ;)

To clarify further, the SimplePhysics one is basically the player's physics code turned into a component. It seemed like a good starting point, but obviously we'll want to get physics api support as well.

We could move towards a component that just handles the physicsRep and passes data to it which is probably the easiest starting point.
#63
01/02/2015 (10:05 am)
@Jeff: hey, yeah your post crossed mine in the tubes I think. :-)

Anyway, it doesn't seem sensible to be to make the actual connection to the API into a component, for the simple reason that you want one master connection stepping the PhysX thread, not one connection per object or even type of component. You could easily have a cloth component and a rigid body component and a ragdoll component, being used on different entities all over the map, but they'd need to talk to some central manager class.

This question is front and center for me right now, and I'd be glad to help you figure it out and implement it this month, because I'm in the middle of a massive refactor of Ecstasy Motion (soon to be renamed indieMotion, btw) in which I drag it kicking and screaming up into the modern world of T3D and PhysX 3.3.1.

T3D hasn't been too bad so far, but PhysX 3 is giving me all kinds of trouble because of the massive changes they made to the interface. While I've dug down this far, I'm also trying to take the opportunity to make some intelligent decisions about whether and if so how to integrate my work with the physics plugin system in Torque, since what I'm using is completely separate and actually predates the physics plugins.

This whole discussion re: interfaces has been illuminating for me, because I basically didn't even know what they were, but it looks like I've sort of been using them all along. My physics system is based on an abstract virtual hierarchy including a base physManager class, which can be inherited by specific implementations (back in the day I used PhysX and then ODE to prove the point, but I'd still like to plug in bullet). I also have similar abstract base classes for rigid bodies and joints, and then make use of all the physics from shapebase-derived classes to provide the actual renderable shape.

It's been working fine so far, but the whole thing seems so radically different from the plugin architecture that I'm not sure how or where to start unifying them, if this would even be a good idea. Meanwhile, though, I've also created a couple of those monolithic kitchen sink classes that desperately need to be shaken apart and refactored into a whole slew of more precisely defined functions, which makes this entity component build a very timely thing for me.

Still actively wrapping my brain around all of it though. :-P
#64
01/02/2015 (10:44 am)
@Dan and Jeff

Thank you for the great information. My concerns are probably mute. It is better to get something in place. Then we can profile it. And find where the real bottlenecks are. I'm really excited with what you are doing Jeff. Thank you for all the hard work.
#65
01/02/2015 (11:47 am)
@ Chris

I think the design of PhysX3 lends itself to a component based usage quite a bit. I'm repeating things you already know, but bear with me: In the PhysX world there is a physx representation of your object and this is often a much simpler version of the object, comparable to the collision geometry. An entire player character, clothing, etc may be nothing more than a cylinder (what are those tubes called with the rounded ends..? fuck.) in the PhysX rep of the world. An entity could have 20 components and none of them are a concern of PhysX. It just needs a simple representation for the entire entity which is where a component would be perfect. The entity will still function with or without it, but will read the physics component for transformational feedback as well as feeding the rep with movement information from the entity itself.

You're right there would still need to be a central wrapper around the physx command/interface, but the individual physx representations would do well to be components in my opinion.
#66
01/02/2015 (11:51 am)
@Chris

Aye, fair enough.

Well, logically you'd want your RigidBody component, joint components, etc. Ideally we make it easy to dictate what physics library is used in the background, so you have one standard set of components and it automagics in the background.

That way if one person wants to use PhysX, they use the same components as a person wanting to make use of bullet and so on.

That's why I was referring to using the PhysicsRep that's already there. That said, nothing stops us from re-implementing the abstraction layer from the ground-up with the E/C stuff in mind. We could easily rewrite it as a core that the components reference to.

@Antony
No problem! I'm glad this thread is getting traffic and ideas flowing, so by all means toss in ideas. If we've already tread that angle, we'll say, but don't hesitate to pop some ideas down. :D
#67
01/02/2015 (12:03 pm)
@ Antony

Quote:
Components could be a limiting factor to how many actors we can have in a scene at once. I'm fairly certain that an actor made from components is going to incur more cost then one without components. The major advantage of components is modularity and reuse. Not performance and memory usage.

This depends on your setup. For instance, when you say "how many actors we can have in a scene" what's the limiting factor you're worried about? I assume you're talking about the bottleneck of iterating through the actors and their components to generate a final list of objects to render? I'm partial to the idea of storing components by component type in predefined arrays for component types that require quick lookups.

Take for instance a RenderComponent. If you pack all the information you need to render that object into that one component (not pointers, actually store the information in the object) and then have a predefined array of all the RenderComponents in the scene, when it comes time to render you just need to iterate through that RenderComponent list with no casting. With all the information stored in each object and the components stored in an array you'll get a contiguous memory layout, and with no pointer traversal it *should* hopefully result in a minimal number of cache misses and could potentially be faster than your standard bloated actor class setup.

In your entity you just have a list of component pointers and you can eat the cost of the cast for infrequent entity update events. Anything done per-frame can use direct component access.
#68
01/02/2015 (12:48 pm)
@Jeff & Andrew: right, exactly! I think we're all kind of saying the same thing, we need a standalone wrapper class for each physics implementation, and then specific components to make use of that wrapper in order to implement various specific functions like rigid bodies, joints, springs, cloth etc.

There could be also kinematic-behavior-specific components which only push data to the physics world and don't have to get feedback from it.

But in any case, it should run through the PHYSICSREP or something like it to separate the actual usage from the backend physics driver.

This is very helpful for me right now (thanks everyone!) on account of the way my current system is structured, I have a "flexbody" class (as opposed to rigid body) which holds together a collection of rigid bodyparts and joints, and which derives from ShapeBase in order to have a renderable/animatable object.

This works tolerably well, but I definitely suffer from the full host of problems associated with a massively bloated kitchen sink class. It would be fantastic if I could just throw a flexbody component onto a renderable object entity, and (can we attach components to components?) have the flexbody manage a full set of bodypart and joint subcomponents, but have all of it completely off of ShapeBase.

I'm going to be fixing our washer today and then driving to Tacoma to do some work on my car with a friend this week, so at some point here will drop out of touch, but please keep the discussion going, this is going to be a front and center project for me for the next few weeks!
#69
01/02/2015 (12:51 pm)
Oh btw, @Jeff, I haven't looked through the full list of your commits yet, but would you say overall that there are (quite a few, some, or hardly any) changes to the engine outside of the component directories? IE if I had an up to date build of T3D I'm working out of, would it be very easy to just drop in your directories or would I have a lot of other merging to do as well?
#70
01/02/2015 (1:43 pm)
Oh also, @Andrew, this is a major thread hijack so if you do deign to answer it please be brief but... have you been back to assimp at all since your first work on it? I'm *very* interested in that project, I haven't been able to spend a lot of time yet and in my first go wasn't successful at importing anything, but the mailing list is very busy! Just curious if you have any plans to hit it again, or where you ended up with it?
#71
01/02/2015 (3:35 pm)
@Chris

There actually are surprisingly few changes to the greater engine code. At least, for now. It'd be relatively easy to move the E/C stuff over to a new build of the engine with WinMerge.

The slightly trickier part is scripts, as there's stuff going on for when clients connect/disconnect and the like.
#72
01/02/2015 (4:29 pm)
If anyone wants to start working on assimp again I'll throw my hat in that ring. Multiple render components: debug rendering? Would that go through a component? Other visual fx? RTS selection ring?

Antony has the right idea- we should do this, profile, and then see how to proceed. Specific components can be optimised like Andrew says. I keep saying I'll make a toy demo with ai characters and so on to have a real stress test. Kind of like WLE's 300 ai test, but with pathfinding, vehicles, whatever else I can throw in.
#73
01/02/2015 (6:26 pm)
@Jeff: okay, cool, good to know.

@Dan, yeah it does seem like multiple render components will want to exist, that would be a primary place where handling components by type rather than by entity would be essential. All components on all entities in current scope with a renderable flag should be handed a GFX handle when it's time to draw, so they can all do what they need to do. I can imagine it would get confusing sorting who should get called first, there would transparency and lighting of other entities and who knows what else to deal with. :-P

#74
01/02/2015 (10:14 pm)
I'll be very interested to see how performance actually stacks up. In Player specifically, there are a ton of features that aren't often in use, and end up adding to the code size. For example, as best I can determine, Player::updateMove ends up being 11KB of assembly. Compare that to a 32KB instruction cache, and take into account that some of those lines are function calls. Part of the idea with ECS is smaller components with specialised functionality, so you end up doing less branch mispredictions and so on. Obviously we're not at that stage yet - but there's room in the future to move to a more memory-friendly layout. But even without that, I'd still not expect components to perform wayyyy worse. Unless your scenes are massive.
#75
01/02/2015 (11:14 pm)
Well just in the way of benchmarking, I decided to see what happened if I ditched my high poly models and stacked a huge array of simple box model entities... I got as far as 46 x 46 = 2116 entities with very little trouble, definitely a framerate cost but I was still in the mid teens (on pretty old hardware). However, at 47 x 47 I started to lose members, there seems to be some kind of cap at 2172 or so, although my framerate was still reasonable. Entities were nothing but base plus RenderShapeBehavior:

for (%i=0;%i<%numPerSide;%i++) {
      for (%j=0;%j<%numPerSide;%j++) {
         %temp = new Entity() {
           position = ((%i*2.5)+%pos_x) @ " " @ ((%j*2.5)+%pos_y)  @ " " @  "0.0";
           scale = "1 1 1";
           canSave = "1";
           canSaveDynamicFields = "1";
           rotation = "0 0 0";
           new RenderShapeBehaviorInstance() {
              template = "RenderShape";
              MaterialSlot0 = "0";
              shapeName = "game/art/SketchUp/smallblock.dae";
           };
         };
      }
   }

#76
01/02/2015 (11:54 pm)
Chris:

Nice. I hadn't pushed it that high yet myself. Good to see it's not immediately crippling, haha. One component I really want to implement in the near-ish future is a batched render component. That would drastically help with performance on entities like that.

As for losing members, you were probably crashing on the ghosting limits. If you consider you had an entity + a render shape component, both of those would be ghosted down.

Coasting past 2k brushes you close to the stock's 4k-ish ghost limit.

That's my off-the-cuff guess though.
#77
01/03/2015 (1:50 am)
Ah, that makes some sense. In normal reality that probably won't be much of an issue, since the render time will cripple me long before the ghosting limit will be an issue, on normal models.

Very interested in that batch rendering, however...
#78
01/03/2015 (6:55 am)
@ Chris

I still work with assimp a lot on a project separate from T3D so my familiarity with the library has increased but I haven't put anymore time into the T3D implementation. If someone is going to take up the torch though I'd be more than willing to help in any way that I can.

@ Jeff

Your render component is calling the render function of TSShapeInstance which adds a render instance to the pass. After they're all collected the render instances are batched by material to minimize state changes. I'm not sure what further batching you could do. You could create a combined vertex buffer but then you need to deal with animated meshes making it only really worth it for static meshes. But then, static meshes will (usually, should) have instancing turned on so that minimizes their vertex buffer memory footprint and/or switching cost.

What did you have in mind for a batched render component?
#79
01/03/2015 (9:12 am)
@ Andrew: orly? That's very good to hear, I expect to be taking you up on that offer! So in general it's been working out well for you then? Do you by any chance work with a lot of fbx import/export? What do you mostly use it for?
#80
01/03/2015 (3:04 pm)
@Andrew - Yes that is my biggest concern. Lots of pointer hopping and cache misses.