Few questions on the engine.

    This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

    • Few questions on the engine.

      Hi Mike and David,

      First of all. Excellent book! Absolutely love it! :D

      Moving on. I've been working on a project for my OpenGL graphics unit at uni where I have to write a simple renderer. I've so far managed to create a simple engine/renderer by adapting some of the systems described in the book (more specifically: scene graph, actor system and the event system) and retrofitted them into my needs. With that, I have a few questions/issues that I'd like some help with:

      # Event system.
      - I've managed to build a modified version of the event management system replacing the FastDelegate library with C++11's std::function. Is this a logical/wise choice?

      - Interestingly, std::function is not comparable with each other (found that out the hard way :P). I've changed the event manager so that every listener must now provide an unique ActorID id. Internally, the EventManager now maintains a std::map<EventType, std::map<ActorID, EventDelegate>>. In an abstract sense, every listeners are now just Actors. Double registration is now checked via checking the existence of the provided ActorID and RemoveListener is now done by giving it the ActorID and EventType instead of the delegate. Any comments or suggestions on this change?

      - The end of chapter 6 briefly described and proposed the use of Events for communication between Actor components (EDIT: within the same Actor). Chapter 11 went on to describe an engine/system wide Event system, but never came back to the proposition in chapter 6. Letting all the Actors and their components to use and register to the system EventManger seem like quite a performance overhead. Imagine there are a thousand Actors each have their AI components listen to a specific event that's suppose to come from another component that's owned by their respective owners/Actor. Actor A's input component queues an Event that all ai components listens for. When the EventManager updates, all AI components will receive that same event, with the majority having to do a check and discard it except one. The check and discard will happen 999 times every time an event with the same type is queued and sent. What is a generally good approach for implementing an event system for Actor and it's components?

      # ActorID and SceneNodes
      - Each SceneNode contains an ActorID to it's owner. That's all good and dandy. But what about Actor's with a bunch of SceneNodes that are all in a tree with a root SceneNode. Do they all have the same ActorID? This obviously cannot happen as the Scene class uses a map to hold all the SceneNodes. But then how does each SceneNode know which Actor they belong to? Should I use a multi-map? :S I think like I've missed out some thing very trivial.

      # Rendering
      - In the book, the draw calls are placed inside the object's themselves. Such as the Mesh class with it's VRender() method. At the moment I've placed my glDraw* calls inside a Renderer class. What do game engines usually do. Do they place the draw calls in the drawable objects or a Renderer class? I am only drawing simple meshes at the moment with simple shader lights. But what would be the proper way to write the render pipeline when I move on to more advanced rendering techniques such as deferred lighting, deferred rendering etc?
      I feel very restricted when writing the rendering stuff at the moment because I fear that I might be doing it wrong and would have to re-invest time to rewrite the rendering system in the future.


      That's all for now. I've enjoyed the book very much so far. I've learnt so much for it. The best part of all is that the book has opened this door into all sorts of different fields that I can dig into. Any comments, suggestions and help with my questions above will be much appreciated!


      Ben
      BenH

      The post was edited 1 time, last by boxy ().

    • I will try to help where I can, here goes

      - I would say that replacing FastDelegate could be a great choice since it is standard, just so long as it; a) offers the same flexibility, b) Has comparable speeds. Not being able to compare them though isn't really very nice however.

      - I would say in my opinion, DONT link the event listeners to actor id's, events are something you are going to want to use all over the place, it is a very generic system. Find a way to compare the functions (if possible), or stick with FastDelegate.

      - Personally, I allow components to access their OWNERS components, but any communication from actor-actor is done in different ways, most of the time, the events are passed to a centralized system ie. When the transform is dirty for an actor, I send out an even notifying all systems that they need to update their representations of the actor, so the physics system will find the rigid body for that actor, and set its position (although you really shouldn't teleport rigid bodies like that if you don't have to), and the render system will find the scene node, and update its transform. Rather than directing events to the components for those actors, I use the systems they are meant to interface with. NOW, if you had AI implemented in a component, it might register to game logic events such as the player moving to loudly, and it would respond by investigating. That sort of decoupling is really handy, because you don't have to have spaghetti code linking objects/players. Instead, when an object makes noise, it just sends off an event and doesn't worry about who reacts to it.

      - This one is an easy one, what I generally do is automatically create a scene node acting as the root of each actor, than when you add your render components scene node, look for the node that is automatically created, and add your new one as a child. This way it is easy to find, keeps your graph nice and clean, and makes removal of an actor much quicker.

      - This one doesn't have alot of material on the web, you can find bits and pieces, and even catch more experienced devs talking about how they did certain things, but rendering engines are usually massive, so it would need a book in itself (or multiple books), just to get you started on it. As far as how the separation between the graph and the API occur, I have found (from my investigations, and experiences);

      - A rendering queue is built up by your graph, this gives a lot of benefits such as easier sorting (transparency, conserving API calls/switches), and also makes multi-threaded rendering much easier.
      - The queue contains render 'operations' which describe either low level commands to be applied, or even high level 'entities' to be rendered.
      - An entity draw operation would define the mesh and materials with things such as; Buffers (vertices, indices), Shaders, Textures, Animations
      - I would even say at this level your rendering could still be abstract
      - The reason this helps with multi-threading, is that all of the OpenGL (or Direct3D) calls will be confined to the thread which processes your queue which saves a lot of hassle (rather than trying to keep everything shared with multiple contexts), everything called from your scene graph is just a common render operation.

      I hope this helps, let me know if you want to go more in depth on a topic.
      PC - Custom Built
      CPU: 3rd Gen. Intel i7 3770 3.4Ghz
      GPU: ATI Radeon HD 7959 3GB
      RAM: 16GB

      Laptop - Alienware M17x
      CPU: 3rd Gen. Intel i7 - Ivy Bridge
      GPU: NVIDIA GeForce GTX 660M - 2GB GDDR5
      RAM: 8GB Dual Channel DDR3 @ 1600mhz
    • Originally posted by mholley519
      I will try to help where I can, here goes

      Thanks! :D

      Originally posted by mholley519
      - I would say that replacing FastDelegate could be a great choice since it is standard, just so long as it; a) offers the same flexibility, b) Has comparable speeds. Not being able to compare them though isn't really very nice however.

      I was thinking the same as well. I chose std::function over FastDelegate because it is standard conforming and well documented. It's also actively maintained and will get improved by it's authors overtime (potentially...). Where as FastDelegate is over 8 years old. There' been no updates, and the author has shown no signs of moving it into C++11. I have no clue performance wise. std::function works for me at the moment so I'm not complaining yet :P I might give one of the C++11 FastDelegate forks a try when I refactor my code after the semester.

      Originally posted by mholley519
      - I would say in my opinion, DONT link the event listeners to actor id's, events are something you are going to want to use all over the place, it is a very generic system. Find a way to compare the functions (if possible), or stick with FastDelegate.

      Why not? To me, comparing 2 functions feels very non-conforming though I could well be wrong. Where as using some sort of ID seems very natural, and in this case I just adopted the ActorID into it. As far as the EventManager is concerned, it's just some kind unique identifiers right? It couldn't care less if it's some ActorID or whatever. It just needs to be unique for each EventType. From an OOP perspective. It also makes sense to identify each listener via some sort of ID right?

      Originally posted by mholley519
      - Personally, I allow components to access their OWNERS components, but any communication from actor-actor is done in different ways, most of the time, the events are passed to a centralized system ie. When the transform is dirty for an actor, I send out an even notifying all systems that they need to update their representations of the actor, so the physics system will find the rigid body for that actor, and set its position (although you really shouldn't teleport rigid bodies like that if you don't have to), and the render system will find the scene node, and update its transform. Rather than directing events to the components for those actors, I use the systems they are meant to interface with. NOW, if you had AI implemented in a component, it might register to game logic events such as the player moving to loudly, and it would respond by investigating. That sort of decoupling is really handy, because you don't have to have spaghetti code linking objects/players. Instead, when an object makes noise, it just sends off an event and doesn't worry about who reacts to it.

      Oh, I think I see what you mean. So you have sub-systems responding to events generated by the components. The sub-system will manipulate the other necessary components (owned by the same or different Actor/s) for you? That makes total sense but I was confused about components talking to components within the same Actor using events. Say if I have an InputComponent receiving control request events from some controller. (i.e. request_jump_event), and the InputComponent wants to tell the PhysicsComponent (if there is one) to apply force and the RenderComponent (if there is one) to begin jump animation. I can access the components directly via GetActor() but what if I want to decouple that connection and just use something akin to events? (sorry if I wasn't too clear on this).

      Originally posted by mholley519
      - This one is an easy one, what I generally do is automatically create a scene node acting as the root of each actor, than when you add your render components scene node, look for the node that is automatically created, and add your new one as a child. This way it is easy to find, keeps your graph nice and clean, and makes removal of an actor much quicker.

      Yes, but you still have to add those new child nodes to the Scene right? but then what ActorID do you assign them?
      Are you saying I just don't add them into the Scene's std::map<ActorID, SceneNode_SmartPtr> ?

      Originally posted by mholley519
      - This one doesn't have alot of material on the web, you can find bits and pieces, and even catch more experienced devs talking about how they did certain things, but rendering engines are usually massive, so it would need a book in itself (or multiple books), just to get you started on it. As far as how the separation between the graph and the API occur, I have found (from my investigations, and experiences);

      It is true. I was afraid I'd get a response like this. I really do understand that this is a huge topic and that the specific implementation detail totally depends on what the developer's trying to achieve. Often graphics API tutorials and materials teach you the API by draw a simple primitive or polygon and then teach you advanced technique all on just that 1 or few objects. What these resources don't teach you is how to render many things fast and how to stage them for different rendering techniques. With the lack of knowing the correct names and terms. I find it hard to find study materials on these topics.
      A good example would be the use of shader programs. I had this impression from the beginning that each object/mesh is suppose to have their own shader program and I did exactly that. When it came to doing the simple ambient+diffuse+specular lights. I had to set the light related uniforms for each object's shader program (besides their transform). This is current a major bottleneck in my renderer because lots of string concatenations have to happen to construct the strings which contain the shader uniform variable names for passing to glUniform. But now when I think about it, it seems to make sense to have different shader programs for different shading passes. This was some thing that I was not aware of and I'm sure that there are many more like this that I'm unaware of. I just find it quite hard to learn things like these with nothing like GCC to kick start me into the right direction D:

      Originally posted by mholley519
      - A rendering queue is built up by your graph, this gives a lot of benefits such as easier sorting (transparency, conserving API calls/switches), and also makes multi-threaded rendering much easier.
      - The queue contains render 'operations' which describe either low level commands to be applied, or even high level 'entities' to be rendered.
      - An entity draw operation would define the mesh and materials with things such as; Buffers (vertices, indices), Shaders, Textures, Animations
      - I would even say at this level your rendering could still be abstract
      - The reason this helps with multi-threading, is that all of the OpenGL (or Direct3D) calls will be confined to the thread which processes your queue which saves a lot of hassle (rather than trying to keep everything shared with multiple contexts), everything called from your scene graph is just a common render operation.

      Sorry, I didn't quite understand the last point :P



      GPU: ATI Radeon HD 7959 3GB

      An ATi 7959? What powerful mysterious ATi GPU are you running!? :O

      Thanks mholley519!
      BenH

      The post was edited 3 times, last by boxy ().

    • Regarding FastDelegate:

      I'll start by saying that std::function and C++ 11 lambdas are perfectly valid choices for an event system. I actually looked in both before settling on FastDelegate. I think it's incorrect to tightly couple it with the actor system; you're only going to cause yourself more headaches down the road. For those who are curious, here's my take and why I chose FastDelegates.

      When you say that's it strange to think of functions as being comparable, it's actually the opposite. Most modern high-level languages allow you to treat functions as first-class objects. Specifically, I wanted to mimic C#'s delegate functions which are a nice, clean way to solve this problem. std::function doesn't quite do this, but it does get a step closer.

      As I said, I looked into using C++ 11's std::function as well as lambda functions when I wrote the event chapter, but they didn't offer what I was looking for. I didn't want an id because there was no simple way to look it up. I wasn't willing to couple the actor system with the event system because the two are absolutely separate. Furthermore, std::function and lambdas are also quite a bit slower than a fast delegate (I did some benchmarks), though probably not enough to significantly matter unless you're sending tons of events.

      Again, what you've effectively done is tightly coupled the actor system and the event system. This type of coupling is generally not desirable. What happens if I want to send an event from one system to another? I can't, I would need to make that system an actor or invent a new id (which would be very hacky). In the version shown in GCC4, you could take that event system and drop it into any game engine and have it work just fine. The bottom line is that your coupling isn't very maintainable.

      Finally, Fast delegate likely won't change much, but it is EXTREMELY well documented. Just open the header file or read the codeproject article and you'll see the author go into excruciating detail on the implementation. You can also open the 3rd party lib folder, find the file, and read the code. Now take a look at the implementation for std::function and tell me which one has better documentation. ;)

      In the end, the choice is yours. If you like std::function better, go for it. :)

      Regarding the actor event system:

      The use-case I was talking about was more about communication within the actor. Specifically, the actor would act as an event system that only its components registered to. That way you could send a single event to one or more actors and not care how they were handled.

      For example, let's say you have a grenade that explodes. You could grab all actors within a radius and send a "grenade exploded" event to those actors (not through the global event system). The actors in turn would broadcast that event to the components registered, in this case the AI component and a health component are registered to receive that event. The health component would damage the player and the AI component would change the behavior.

      Without this, you'd end up grabbing the actor, then getting both of those components from the actor. You'd check if the component existed and, if it did, you'd call some function to get the desired result. This tightly couples those components to the grenade.

      Turning actors into mini-event systems is a great way to handle that. The downside is that it's more overhead per actor, so if you do have 1000 actors, it might be a bit much.

      Also, when the grenade explodes, you would never want to send a global event and just test out 99% of the actors. You'd have some kind of space partitioning system, like a quadtree or octree, to handle choosing which actors to notify. Once you have the list of actors you needed to affect, you could either use this method of notifying the actors or send a global event caught & processed by another system that updated things.

      For example, I have component systems that I allow components to register to. When an actor is created with a component, those systems are notified and store a weak ref to that component. Thus, when a grenade explodes, I can send a global "grenade exploded" event that is caught by my two component systems (or for AI and one for health) to update the appropriate actors.

      There are many ways to solve this problem. I've seen a number of them used before, but the one that's most common is to just grab the components and update them directly.

      -Rez
    • Hi Rez,

      You're very right! I had a look at the source code and it's extremely well documented :P I spoke way too soon. Still, I find it a bit off putting that the library hasn't seen much (if any) love from it's author at all for the last 7 or 8 years.

      I can see why having an ID system for the EventManager could be hacky, but when I first thought about it. It sounded pretty good in terms of performance. Both removing and retrieving now run in log time by using a map (the event system in GCC used a list if I remember correctly). And instead of comparing delegates, we only compare the ID. It comes at the expense of the listeners having to hold a copy of that ID if they want to ever de-register from the event system.


      The use-case I was talking about was more about communication within the actor. Specifically, the actor would act as an event system that only its components registered to. That way you could send a single event to one or more actors and not care how they were handled.

      Apologies for not wording my original question properly. What I wanted to know was. What is generally a good way to do that.

      Turning actors into mini-event systems is a great way to handle that.

      Do I just let Actor inherit from EventManager?



      For example, I have component systems that I allow components to register to.

      Wow, that sounds crazy from a naive point of view. You would have as many ComponentSystems as there are different types of Component!
      BenH

      The post was edited 2 times, last by boxy ().

    • Originally posted by boxy
      You're very right! I had a look at the source code and it's extremely well documented :P I spoke way too soon. Still, I find it a bit off putting that the library hasn't seen much (if any) love from it's author at all for the last 7 or 8 years.

      That's because he hasn't had to. Still, a lot of engineers are leery about using code they find on the internet. I totally get that and used to be the same way. To each their own. :)


      I can see why having an ID system for the EventManager could be hacky, but when I first thought about it. It sounded pretty good in terms of performance. Both removing and retrieving now run in log time by using a map (the event system in GCC used a list if I remember correctly). And instead of comparing delegates, we only compare the ID. It comes at the expense of the listeners having to hold a copy of that ID if they want to ever de-register from the event system.

      You can't think about performance until you're actually trying to solve a performance problem, especially when you're just starting out. You won't know which is better. In fact, I would be willing to bet that your system is slower overall.

      For example, look at these two functions. They both swap two variables. One does it with a temporary variable and the other does it with XOR.

      Source Code

      1. void Swap1(float& left, float& right)
      2. {
      3. left = left ^ right;
      4. right = left ^ right;
      5. left = left ^ right;
      6. }
      7. void Swap2(float& left, float& right)
      8. {
      9. float temp = left;
      10. left = right;
      11. right = temp;
      12. }
      Display All


      Which one is faster? Most people will tell you the XOR method is faster since you're not storing a temporary variable. I've seen articles telling you to always do this because it's better. The reality is that it's usually not, the temp variable is almost always better.

      Assumptions and premature optimizations are very bad and always cause more harm then good.


      Do I just let Actor inherit from EventManager?


      That's the simplest way to do it, yeah. The event manager is a bit heavy weight for what you'd need there, but it's a good place to start.


      Wow, that sounds crazy from a naive point of view. You would have as many ComponentSystems as there are different types of Component!

      Not exactly. Most components don't need systems, only the ones that actively process stuff every frame. I don't have an Update() function on my components; meaning that my actors never take up per-frame CPU time unless they have to. I think I have maybe five or six systems in the entire engine, compared to the 30+ components I ended up with. For example, the TransformComponent doesn't need a system, nor does the ScriptComponent. None of my Lua-based components are ticked either, so they don't have anything.

      If you're interested, I'd check out this post:
      mcshaffry.com/GameCode/thread.php?threadid=1916&sid=

      I go pretty deep into the component system I built for my own engine. The one I built for GCC 4 is loosely based off of it.

      -Rez