How low level should events be?

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

    • How low level should events be?

      I have a question. Based on this architecture, if there are 100 objects in the scene, all moving, then the event manager is throwing around 100 matrices per frame for a move_entity call. If we add a color to each object and sprite animation (let's say this is a 2D game), then we're potentially triggering hundreds of events per frame.

      My gut instinct is to want to get rid of these lower-level, high frequency events and just have the scene node and entity share a shared_ptr of data. It seems like this would break the networking and event replay architecture though.

      So I'm torn. Is this how game engines really handle things for games with lots of objects?

      I guess I'm not sure whether events should be higher level things like "Player died", or "Player entered aircraft", rather than "player_moved" or "update_player_color."

      Thanks,
      Zach.

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

    • I think this is a case of trading performance for readability/expandability. As you mentioned, directly coupling things would cause some problems with networking and such, and might also limit your ability to add functionality in the future (or at least make it harder). Where as leaving everything decoupled will have more overhead, but should be a bit easier to understand (albeit harder to step-through debug) and expandable. Given the huge amount of cycles spent processing things like graphics and physics though, I don't think a few hundred events per frame is going to compare to computing physics for those 100 objects.

      Rez touched on this issue in About the event manager
    • rickvanprim is absolutely right, it's a trade-off and a few hundred messages probably won't be your bottleneck.

      If you read through the thread that rickvanprim linked to, you'll see that I talk about using a priority queue and how you could run into an issue of starvation. One solution to the event starvation issue might be to simply have two priority levels. High priority requests are all processed every frame regardless of how long it takes while low priority requests use the priority queue technique to ensure that they don't eat up a lot of the frame's time.

      In the end, I'd do it the easy way and profile later. Premature optimization is never a good thing. Unless you're doing something crazy, you'll probably spend 80% of your time rendering the scene. If you split that into another thread, the amount of time it takes for events to process becomes trivial compared to figuring out how to efficiently batch your render calls.

      -Rez
    • Well, right now since my game logic and rendering code are effectively cut off from each other, sending events is the only good way to communicate between them. Sometimes that means sending duplicate data from the actor to the scene node. I guess I wasn't sure I liked that idea.

      Like you said though, it's a tradeoff. I would really love the flexibility of pumping events into a buffer and then playing them back, or simply sending them over the net when necessary.

      I feel better about it now. Thanks guys!
    • Be careful with keeping duplicate data. When I was at Planet Moon, our engine maintained THREE different matrices describing an actor's transform. One was the render transform, another was the logical transform, and the third was the physics transform. This caused a LOT of headaches.

      In my own personal engine, I have a special render thread whose only job is to constantly render everything in its queue. the logic side (main thread) is the authoritative keeper of that data. It fills a queue with render data objects that contains copies of the data required to render it (transform, texture pointer, etc). The render thread consumes this queue, rendering each object and discarding the information at the end of each frame. The information is rebuilt every frame so any stale data only lasts for one frame.

      -Rez
    • One thing that renderers typically do is batch commands together - so in order to maximize performance without nuking the event based model, a "batch" movement event could be created, say from the physics system, that moved all objects under physics influence. If the event were local, and not networked based, the movement buffer could even be implemented in shared memory so that it doesn't have to be copied around.

      It's a little more complicated, but tends to keep things moving along pretty good.
      Mr.Mike
      Author, Programmer, Brewer, Patriot
    • With my Engine which I integrated some systems from the book such as the game logic and rendering separation and the events system, I had some hundreds of dynamic physic objects interacting and events being sent for each, didn't seem to be a problem.

      still, in my opinion the physic system should be able to directly call the gamelogic's move and renderer's move functions without an event.

      For networking you do not want to send actor_moved event anyway, you want to sent actor_velocity and update positions only every now and then. Otherwise you get a very laggy an inefficient network code.

      youtube.com/watch?v=sZxdN_15i2k&feature=player_embedded
      At about 2:30 this video you can see dynamic objects being added, quite a bit of them. The sudden freezes are due to voxel terrain editing, we fixed that :)

      The field of grass is also defined as entities, quite a bit of them but without physics entity (which would be a bit silly, unless for later interaction effects).

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

    • Mike,

      I've heard of that architecture before. I'm curious how it fits into the Logic/View system that you develop in your book. I've noticed that the Scene Graph implementation is limited to rendering single subsets of objects, i.e. a single mesh, a single skybox, etc. Adding instancing doesn't seem trivial with this approach.

      I'm just thinking out loud here, would you have rendering system separate from the game logic and scene nodes that you send batched commands to? Then, rather than a scene node drawing its own mesh, it could send a request to the rendering system to draw the mesh, sorting it by material or whatnot.

      I'd like your thoughts on my engine architecture. I've thought through a lot of things, but my inexperience makes it difficult to see holes in the design.

      My engine is for casual 2D games (something that could eventually be used to make a game like World of Goo, for instance). The architecture I've chosen is to allow the user to inherit from the scene class and implement an event handler for the game. The scene is basically a visual parallel to the logic. The goal of this design is to allow decouple the rendering and logic to the point where you could remove the scene entirely ad the game would function (without rendering, of course), or revamp the graphics entirely without really needing to touch the logic.

      This means that the logic and scene both receive identical game events, but the logic is responsible for moving an object, while the scene renders it with proper animations/effects, etc. Since my engine is in 2D, I have a base Sprite class and an inherited AnimatedSprite class that most scene nodes can inherit from.

      Additionally, I changed things a bit from the book. I put the scene in the App class, since it is at the same level as the logic. The view can have a pointer to the scene, and it is responsible for the camera into the scene and the viewport in the game window. I'm hoping this would allow for multiple human players on the same screen.

      Is it realistic to try and decouple rendering and logic to that extent? I've seen other engines that allow users to set animations and textures and handle game logic in the same function. My design would require the logic to handle itself in the Logic class, and the rendering code to handle itself in the Scene class, all based on events.

      What do you guys think?
    • Without seeing any UML diagrams or your code, the architecture sounds pretty good. And yes, it is not only possible to decouple rendering and logic to that extent, it's very desirable.

      In my own personal engine, I have an Application base class that has a pointer to a Scene interface and another pointer to a Logic interface. These interface classes have no data; they have nothing but pure virtual functions needed to perform basic tasks. Here's my Scene interface:

      Source Code

      1. class Scene
      2. {
      3. public:
      4. virtual ~Scene(void) {}
      5. // This function initializes the object and gives the implementation class a chance to instantiate / initialize
      6. // anything it needs. It's called once in Application::Init().
      7. virtual bool InitScene(void) = 0;
      8. // This function initializes the renderer. It is called once automatically by Application::Init() but may be
      9. // called anytime during execution, as long as DestroyRenderer() is called first. This allows the application
      10. // to reinitialize the renderer after destroying it, such as when the graphic settings change.
      11. virtual void InitRenderer(void) = 0;
      12. // This function destroys the renderer, but NOT the Scene object (which is only destroyed in the destructor).
      13. // It should always be called in the implementation class's destructor. The purpose of this function is to
      14. // allow the application to explicitly destroy the renderer at any time, modify some graphical settings, and
      15. // call InitRenderer() to bring it back up again.
      16. virtual bool DestroyRenderer(void) = 0;
      17. // Called by the application to add a render stage to the scene. The caller is responsible for instantiating
      18. // the RenderStage object. Ownership of the RenderStage object is passed to the Scene after this call.
      19. virtual int AddRenderStage(RenderStage* pStage) = 0;
      20. // This function adds an object to the scene for rendering next frame. Ownership of the RenderData object is
      21. // passed to the scene.
      22. virtual void AddObjectToScene(RenderCommand* pObjectData) = 0;
      23. // This function is called every frame by the Application in the main thread. Single-threaded implementations
      24. // should perform all rendering here. Multi-threaded implementations should sync up with the render thread
      25. // if necessary (note that this function's caller is not thread-safe by so if you need to sync threads here,
      26. // you'll have to make the implementation of this function thread-safe).
      27. virtual void Render(unsigned long deltaMs) = 0;
      28. };
      Display All



      I inherit from those interfaces to create a specific implementation and instantiate whichever implementation class is appropriate. For example, I currently have two different kinds of Scene implementations. One spawns another thread and does all the rendering in this thread. The other is a simple scene that renders everything on the main thread. It also has a much simpler (and slower) way of rendering the scene by just walking a list. I can switch between these seamlessly by changing one function call.

      There's an old programming architecture saying that goes like this: program to interfaces, not implementations. Nothing outside of the scene has any clue that there's another thread rendering everything. All they know is what the interface tells them. They submit their RenderCommand objects to the scene by calling AddObjectToScene() and assume that their object gets rendered when the time comes. They don't know nor do they care how it's processed. Why, I could even make a new scene implementation that renders everything in text without even touching any other code. I just make a new TextScene class and inherit from the Scene interface. The only thing I have to do is actually instantiate the new class and assign it to the Application's Scene pointer.

      This is the level of abstraction you should be striving for with all your major systems. Systems should have a very thin interface to each other to minimize the amount of coupling that can occur. Even then, you should minimize the number of interfaces any particular system knows about in case you need to rip one out completely.

      Most professional games try to do this but often fall short for a number of reasons, usually due to the sheer number of people making code changes every day (many of them juniors). When I was at Super-Ego Games, we made a render skin to abstract out the rendering stuff, similar to what you're doing here. My job was to write the NullRenderSkin, which is just what it sounds like. It crashes the game with a NULL pointer on the first frame, but it works and absolutely NO rendering stuff is linked in.

      One last thing: this doesn't apply to just render vs logic. It applies to all systems that can be contained. For example, here at EA on The Sims Medieval, we have a nice pathing system that has a simple public interface. Basically, you make a function call to it saying "route me here" and off you go. You'll get a callback for whether or not it succeeded once the path is done. How does it work? Who cares! Only AI programmers like me have to know the in's & out's of that system. Everyone else just needs to know the public interface.

      Hope that helps. It sounds like you're on the right track. :)

      -Rez
    • Thanks Rez,

      I'm intrigued by your RenderCommand design. I know you said that your game passes commands to the scene each frame, so it must have some kind of internal job queue where items are popped off and rendered. I'm guessing that you don't instantiate entire render objects each frame? Do you have a component attached to your entity object that you pass in?

      Also, since your renderer draws things in a separate thread, do you draw everything in the game in that thread (i.e. HUD info, menus, etc)? I guess you'd have to unless you share contexts or something.

      How do you handle batching? Lets say you have a couple render command to draw muzzle flares for guns in the scene. I'm assuming you sort by material somehow, but then is your renderer smart enough to instance those individual draw calls? I'm a little hung up on how to break out of the "one draw call per node/command" paradigm. It seems like most commercial engines have some sort of batching mechanism that fits naturally into the architecture.

      Finally, which system has authority to send commands? Does your logic do that?

      Thanks for your help. I really appreciate your responses.
    • First, a disclaimer: I am not a graphics programmer by any stretch of the imagination, my specialty is AI (hence why I wrote the AI chapter of Game Coding Complete). I'm sure there are much better ways of doing this. ;)

      On the top level of the rendering system is the scene. The scene owns a list of render stages. The render stages each have a queue of RenderCommand objects that contain the information needed to actually render a single object. In reality, the RenderQueue class is implemented as two queues. One queue is manipulated by the main thread, the other by the render thread. As the main thread does its processing, it adds objects to the main thread queue. The render thread pops objects off the render queue for rendering. At the end of the frame, there's a synchronization event where the main thread queue and render thread queue switch places; the main thread queue becomes the render thread queue and the vice-versa. The main thread then destroys all RenderCommand objects in its queue (which used to be the render thread queue) and proceeds to run another frame of events while the render thread renders its new queue in parallel.

      The RenderCommand objects aren't exactly instantiated & destroyed for each rendered object, they are maintained in a memory pool. I overload the new and delete operators for the RenderCommand class to pull from this memory pool to keep from incurring the cost of allocating & deallocating the memory hundreds of times each frame. That's why it's important that they the new and delete calls happen from the same thread. It keeps me from having to make the memory pools thread safe.

      Entities are built using a component architecture. An Entity is essentially just a aggregation of components (Game Programming Gems 5 has a pretty good article about component architecture if you're interested). Examples of components include ScriptComponent, AiComponent, RenderComponent, etc. Each of these components is tied to a ComponentSystem, which is responsible for actually updating that component. During the RenderComponent's update, it will do some simple calculations to determine if its visible (right now, I'm only checking the camera bounds) and if it is, it'll create the RenderCommand object and pass the info to the scene. None of the other components even know that this entity can render itself.

      Yes, everything is rendered on the render thread. It has to be since the render thread is the one that creates the Direct3D device object.

      As for batching, what little I do is all handled by the render stages. The render stages are each processed in order and define the sorting method for their internal queue. They also perform the actual render calls so they can process their queues however they wish and perform the rendering in whatever way is appropriate. Right now I instantiate three RenderStage objects. The first is for opaque objects and is sorted by material. I have a single static vertex buffer that I reuse over and over so I don't batch the render calls, but the sorting does allow me to cut down on the number of state transitions (in this case, SetTexture() calls) that need to be made. I could probably batch the render calls pretty easily but it hasn't proven to be much of a bottleneck so I haven't bothered. The second stage is for translucent objects and is sorted by a combination of Z and Y+Height (remember, it's a top-down 2D game). The third stage is sorted by Z without regard for Y and is used for UI rendering.

      Keep in mind that my engine is really a 2D engine, which is why I can get away with a single static vertex buffer. Batching is not that much of an issue for 2D games. Let's say you're rendering an animating sprite. You basically have two options: the first is to have each frame be a different texture. The advantage to this is that you can have one vertex buffer that you use over and over for every object. When you render each object, you only have to call SetTexture() on the appropriate texture and then render your geometry. This means one state change and one draw call per object but you get a nice, simple vertex buffer that never changes. The alternative is to have each sprite have its entire frame set inside a single texture and change the UV's each time you render it. This means for each one of these creatures, you can set the appropriate UV's and render them all in one batch. The disadvantage is that every frame you have to lock the vertex buffer, update the vertex data, then unlock it, set the texture, and render. It makes your render code quite a bit more complicated and what does it save you? How many of that single sprite are you going to draw? 10? 20? 100? What are you really saving? In a 2D game, you're not going to be bound by number of render calls, you're probably going to be bound by texture fill-rate.

      This all changes when you have a 3D game. Batching is critical for 3D performance because of how much stuff you end up trying to render. My basic architecture is still sound though and a lot of 3D engines do something similar. You batch all of the opaque renders together by material since z-buffering will take of their depth sorting, then you manually sort all translucent objects because z-buffer can't help you, then you render a 2D layer over that for UI. In fact, all you'd really have to change here is how the vertex buffers & index buffers are used. Of course, you also need a MUCH more sophisticated culling technique, which is where things like BSP trees come into play. The fastest triangle is the one you don't draw.

      RenderCommands are created by any system that needs to create them. Right now, that's the UI, the particle system, and the RenderComponent for entities. They are created in the main thread during what might be considered the logic update, though the gameplay systems really have no concept of rendering.

      Hope that answers your questions. Let me know if you have any questions. :)

      -Rez