Engine structure and design

Coordinator
Oct 6, 2007 at 8:29 PM
This thread is to discuss the engine structure and design. Any changes an additions to it should be discussed here.

Relevant material, such as UML diagrams, statecharts, sequence diagrams, should be posted here as well.
Oct 6, 2007 at 10:09 PM
Since we're working with .NET, it makes sense to try to split things up into separate modules. If done right, it'll help to reduce inter-module dependencies and generally clean up the design. It will also help to support multiple executables using the same code-base, like editor and game.

I'm thinking something along the lines of:
QuickStart.Engine - Basecode assembly used by every other module
QuickStart.Game or QuickStart.World - Generic game-logic. Not applicable to a specific game, but implements common functionality.
QuickStart.Graphics - The renderer. Handles all rendering details.
QuickStart.Physics - The physics back-end
<whatever else we add/need later on>

This is just a very high-level design idea, but it gets the point across. Obviously there will be some inter-dependencies between modules, but it'd probably be best to try to eliminate them as much as possible, and use interfaces instead of direct references when dependencies are needed. This way, if a client wants to write a game, they are free to pick and choose what components they want to use, and can easily replace components with their own that are more specific to the game they want to make.

As far as what clients can see, I'm wondering if it would be a good idea to expose an abstract class derived from Microsoft.Xna.Framework.Game. i.e. a class is provided in the engine that derives from Game and implements whatever back-end processing we need to do. Then, clients derive from this class (let's say QuickStartGame) to implement their own code. This way, we can put whatever we want in our abstract version, and all the client needs to do is make sure they call base.Update() and base.Draw() in their overloaded Update() and Draw() methods.

At the end of the day, I think it would be easier on clients if they just had to include references to the assemblies of the components they want to use, instead of having to pick through source files to decide what they want, and then run into dependency problems.


Coordinator
Oct 7, 2007 at 12:08 AM
I agree with pretty much all of that. It is a great idea to abstract the game class. The less code the client has to see or change the better.

I've tried to keep the components separated, but actually giving them a different namespace is actually a goal I was hoping for later. So doing something like QuickStart.Physics would be perfect.

Rather than work on A.I. right away it may be a good idea to work on design instead. I probably won't have time for a few days to start a UML diagram, but you're welcome to uml any ideas you may have. I think we should get a UML going, and then post it public for critique. This way we can both critique it, but others may have input.

I figure when we get some more functionality that users are looking for, like players/bot, goals, interactions other than physics, we'll get a higher participation on the project. But it would be good to get the design base finished before we added a lot of things, it would save us a lot of time from re-writing a lot of code.

While on the topic of design, I was thinking there should be a better way to call .Draw() then what is currently being used. Currently to have all components be able to be drawn off of the same call, I have to pass them all parameters that some require. For example, if I have terrain, and entities, they both need view, projection, lighting. However terrain needs the scene's camera for view culling. Rather than pass all variables to each, I think we should place all dependancies in the constructor. For something like terrain, I could place the scene's CurrentCamera in the constructor, and give the terrain a copy of the camera, then the terrain component could have a reference to the CurrentCamera whenever it needed and it wouldn't need passed during the draw call. This would save passing many variables to possibly hundreds of entities every frame. Having the dependencies in the constructor would keep the user from loading components without all the proper dependencies.

These are just the types things that need UML'd.
Oct 7, 2007 at 4:51 PM
I'm going to start prototyping some ideas. Seeing actual code and how it fits together seems to do more for me than staring at UML diagrams.

Two things that immediately jump out at me with the current code is the Component base class, and the direct dependencies between objects.

Having a base class for dynamically created objects is a great idea, I'm just wondering if everything needs to be derived from it. For instance, I'm not sure if the subsystems (input handler, physics simulator, renderer, etc) need to be defined as Components. I think a distinction needs to be made between subsystems and game entities, as subsystems are fundamentally handled differently.

As far as the direct dependencies, eliminating as many of these as possible should be a primary goal. For example, the current input handling class directly makes calls into the camera, physics, and game classes. I believe an event system would be preferable here. The input handler can process key presses and then use a mapping (defined by both the engine and client code) to generate events which are sent to the client and engine back-end. This way, game clients can say, "if key A is pressed, send me a FIRE event". Similarly, the engine can be monitoring for such events. This can easily extend to the whole engine, as well. For instance, the physics simulator resolves a collision. Instead of interfacing with the sound manager itself, it just sends a message saying "objects A and B collided." Then, the sound manager can pick up this event and play a collision sound based on the objects' properties. Similarly, if the two objects are a player and a falling box, the player entity can pick up the event, and decrease from the player's health. All this requires is a common interface for event/message passing, and practically eliminates all inter-module dependencies. The beauty of this is clients are then able to pick and choose what they want to use from the engine without worrying about dependencies.
Coordinator
Oct 7, 2007 at 5:47 PM
Sounds good to me. I've never liked having to give input access to everything, it meant created public accessors to private methods solely for input.

Looking forward to seeing a prototype and testing it out. In the mean time I think my top priority should be getting the terrain issues worked out. Some are having problems I believe are related to the index buffer, others like yourself had issues with the terrain custom vertex type and shader. By the way, did your patch allow terrain to render properly? Or did it just allow the program to run without crashing?
Oct 7, 2007 at 5:55 PM
The terrain looks very similar to the posted screenshots, so it appears to be working.
Coordinator
Oct 7, 2007 at 7:17 PM
That's great, thanks for the fix. I'm assuming the normal mapping is not working, being that you're filling the binormal and tangent with blank vectors?

I'm going to research normal mapping further. I would think that knowing the normal, light direction, and camera position, and having the normal map texture would be all you need to get the normal mapping effect. It seems that way on my machine.
Oct 7, 2007 at 7:41 PM
It depends on the type of normal map you use. Typically in games, you see tangent-space normal maps. They are recognizable by their characteristic blue hue when viewed (the normal maps in your project look like tangent-space maps). With these, the normals (in the normal map) are stored in tangent-space (Normal, Binormal, Tangent). That's why you create normal/binormal/tangent vectors such that they create an orthogonal vector space. When you read in the bump map, you get an x/y/z vector in this space, not world space or object space. You then translate this vector by the tangent-space matrix (created from your normal/binormal/tangent vectors, per vertex) to get the normal in world space (or object-space, if you didn't originally transform the normal/binormal/tangent vectors by the geometry's transformation matrix). What seems to be happening in your code, is that the normal is correct, but the tangent/binormal data is not (right now, they'll be zero vectors). So, when you do the bump mapping, you're actually getting the projection of the real normal onto the vertex's normal direction. So, instead of getting the true normal vector, you're essentially getting the dot product between the normal-map's normal and you're normal. I know that probably sounds confusing, I wish these boards supported latex mathematical syntax. But essentially you're a third of the way there, that's why it looks like its doing something. You just need to calculate the real binormal and tangent vectors, and then you'll be able to extract and transform the real normal from the tangent-space bump map.

Oct 7, 2007 at 8:23 PM
Edited Oct 7, 2007 at 8:24 PM
I found an old school project that I wrote demonstrating tangent-space bump mapping, including generation of tangent/binormal vectors. Unfortunately, it exhibits the same damn GLSL errors that I had with the water demo I posted on the XNA boards a while ago for you, with the volumetric fog in the water. I'm starting to think its a bug in the GeForce 8-series OpenGL drivers. I posted a thread on GameDev.net about it, I'll let you know if I get it resolved.

If you want to take a look, you can find it here: http://www.cse.ohio-state.edu/~holewins/781/781_lab3.zip

Maybe it'll work for you as you're on a 7-series card.

If you do get it to work, it needs some command-line arguments. Read the README file for directions.
Coordinator
Oct 7, 2007 at 8:39 PM
Ok, so first priority is figuring out the index buffer issue on older cards, then I'll look into proper normal mapping. If the vertex is going to need a binormal and tangent then I assume that is going to increase the vertex buffer by 24 bytes per vertex ( 3 for a vector * 4 bytes * 2 vectors )?
Oct 7, 2007 at 8:53 PM
Well, that depends. You have two choices. You can either optimize for video memory savings, or GPU calculation savings. Since the normal, binormal, and tangent vectors represent an orthogonal vector space, you can easily calculate the binormal in the vertex shaders as:

binormal = normalize(cross(tangent, normal));

You can also do that same for tangent, given binormal and normal. So, you can either calculate both the binormal and tangent in the preprocessing step and expand the vertex stream by 24 bytes per vertex, or only find one in the processing step and calculate the other in the shader, needing only 12 extra bytes per vertex.

If the vertex data is going to be streamed to the card every frame, I'd say push the calculation to the shader and save yourself some AGP/PCIe bandwidth. Otherwise, if the vertex data is stored on the card and unchanged, you might as well just store the extra 12 bytes in the vertex data. Video cards nowadays have plenty of vram to go around.
Coordinator
Oct 8, 2007 at 7:12 AM

shawmishrak wrote:
I found an old school project that I wrote demonstrating tangent-space bump mapping, including generation of tangent/binormal vectors. Unfortunately, it exhibits the same damn GLSL errors that I had with the water demo I posted on the XNA boards a while ago for you, with the volumetric fog in the water. I'm starting to think its a bug in the GeForce 8-series OpenGL drivers. I posted a thread on GameDev.net about it, I'll let you know if I get it resolved.

I got an error as well: Shader does not contain 'baseTexture' uniform.

I did notice your readme stated to download textures from another site, which is a broken link. However your zip file included models and a texture, so I'm not sure what is going on.
Oct 8, 2007 at 1:52 PM
Oh, yeah, you don't need the extra .zip file. I repackaged it all together.

There's something weird going on with the GLSL code, I'll try to fix it.
Oct 8, 2007 at 3:50 PM
I've been working on a prototype, and I've come to a decision that will heavily influence all future design. The question is whether subsystems should be broken down into separate assemblies. Either way has pros and cons. Namely,

Separate Assemblies
Pro: Clear distinction between modules
Pro: Ability to work on modules as separate projects
Pro: Clients can implement their own modules based on defined interfaces
Con: virtual-calls everywhere
Also, with this method, clients can either directly reference all needed modules at compile-time, or we can provide the ability to dynamically-load modules at run-time (does work on Xbox too).

One Assembly
Pro: Compiler/runtime are better able to optimize/inline calls
Pro: Only need to worry about including one assembly in the project.
Con: Monolithic build, everyone working on same project.
Con: Clients may have a more difficult time picking and choosing what they really need, as inter-dependencies may occasionally arise.

Let me know your thoughts/ideas. The beauty of the multiple assemblies approach is that interfaces will be defined for all subsystems, and clients can freely choose to re-implement any of them with their own implementations, more suited to their needs. However, the cost of this is increasing the number of virtual calls, which can add up to hurt performance if enough virtual calls are made per frame. At the moment, I'm really not sure which method I believe to be better for this project. However, with the separate assemblies approach, a conscious effort must be made to reduce inter-module calls, so virtual calls are kept to a minimum. I remember reading somewhere (I think it was the compact framework team blog), virtual calls on the Xbox CLR can be painfully slow as they are implemented using a look-up table instead of the usual jump-table. Frankly, this scares me. My main concern at the moment is Xbox performance. I'm not too worried about Windows, as the .NET runtime is pretty optimized. However, I wrote an Xbox game for a school project, and we were always struggling to get the Xbox to perform acceptably, while the Windows builds were just fine.
Coordinator
Oct 9, 2007 at 4:29 AM
If I remember right, the fastest is inline, then function calls, then virtual calls, and then 3 others afterwards that are even slower.

I think we should go with separate assemblies, however, only if it can be done in a way in which we believe that performance won't suffer more than a few percent at the most.

Off topic I want to appologize for my lack of help recently. Not only did I just finish a week of finals, but my job recently told me that I have to become a Lua guru.....and I've never used Lua, so I've got that task ahead of me. I assume within a couple weeks I'll have the hang of things, but during that time it'll be hard for me to concentrate on this. I wish we had more participants like yourself. It's nice to have this project open on codeplex because it can be worked on while others are gone, we just don't have a whole lot of participation right now, and I fear if we stopped for awhile noone else would be interested.
Oct 9, 2007 at 6:43 AM
It would be extremely nice if they would publish some sort of documentation on exactly what the Xbox CLR does and does not do as far as code generation and code optimization. Everytime the issue comes up on the XNA forums, I always just get links to the Comfact Framework Team blog, which is insightful, but it doesn't have the details I'm looking for, especially since you can't get PowerPC assembly code dumps on the Xbox.

No need to apologize, I understand perfectly. I'm a grad student myself, and I use to work at a utilities company as a software developer. It's not like I've been doing very much either (for the same reasons, I've had some assignments due over the last week or so). I've spent a couple of hours prototyping some things, and looking over the code to get an understanding of the current design, but that's about it. It's mainly just mental exercises trying to think my way through the design and find flaws. I've been wanting to start participating in a real XNA dev team for awhile now, just to see how far I could push the technology, and this seems like it will be a great opportunity. I think it's obvious by now that XNA as an Xbox platform can handle the SNES-era 2d games, but I really want to see how far a talented group of programmers can push it in 3D.
Oct 9, 2007 at 6:46 AM

LordIkon wrote:
I think we should go with separate assemblies, however, only if it can be done in a way in which we believe that performance won't suffer more than a few percent at the most.


I agree, it's just the "black box"-ness of the Xbox that worries me. With C++, I know what the costs of different kinds of function calls are and how to optimize them, but with C# on the Xbox, it's all trial and error.
Coordinator
Oct 9, 2007 at 7:01 AM
I won't have a 360 or creator's club $99 membership untli around Christmas either, so I couldn't help you test on the 360. Not only that, but once I get the 360 it is going to take me some time getting used to testing/debugging on it. From what I've read it can be different in quite a few ways, aside from the performance issues. It will be exciting to integrate 2.0 into the engine though. I'm trying to get a couple classmates to participate on the project, however one guys wants to concentrate on Flash and action scripting, the other is wanting to concentrate on Ogre, neither of which will be of any use to us, and neither currently integrate with C#. I'm still trying to turn them to the "dark-side" that is C#. There are some very talented people in the community making neat stuff. I've seen some great videos on YouTube, and some excellent samples (aside from the creator's club samples). However I believe some of them are by actual indie developers, or others that just are interested on working on either a team effort, or something that is open-source.
Oct 9, 2007 at 3:39 PM
I must admit that I am not sold on C# yet. There's definitely things about it that I don't like, and I still consider C++ my language of choice. However, that being said, one of my primary aims of joining this project is to see how far we can push C#/XNA. I want to see how far we can go before we hit a roadblock that is caused by the CLR.
Coordinator
Oct 10, 2007 at 3:15 AM
It's tough to say, if we optimized even the program we had now, and threw in geomipmapping I'd be at 60fps at any time on my machine. I can get up to around 300 entities all checking collision on each other without any oct-tree culling, which means ALL entities checking each other every frame, before my framerate starts to take a hit. Figure there probably won't be 300 entities in view and interacting at any time, and after optimizations, and I believe a decent engine could be made, even with A.I., animations, and 3d models in the scene.

Of course, I'm think in terms of PC, not Xbox360 with garbage collection issues.
Oct 10, 2007 at 4:09 AM
Alright, we might as well go ahead with the "modules in different assemblies, using interfaces" approach. It's definitely the cleanest and most modular. Worst case, it wouldn't be all that hard to refactor the virtual inheritance out later, if we had to. Still, I just wish I had a better idea of the implications of this on Xbox. I'm sure we'll figure it out in time, though. I'm anxious to see what Xbox CLR changes come with XNA 2.0.
Oct 10, 2007 at 4:15 AM
Also, I found a link to an interesting article in Shawn's blog. It would probably be a good idea to decide up front which approach we should probably take.

http://blogs.msdn.com/shawnhar/archive/2007/07/02/twin-paths-to-garbage-collector-nirvana.aspx
Oct 10, 2007 at 4:22 AM
Sorry, one more thing. Do you have any idea how branching works in TFS in conjunction with the "change sets" on this site? I want to create a separate branch for the prototype, so its separate from the rest of the code, but for all I know anything I do will just end up as another "change set" on the web page.

I really miss SVN...
Coordinator
Oct 10, 2007 at 4:42 AM
Not sure, I've barely learned the CPC.

As soon as we convert to XNA 2.0 I believe we should go with something that is integrated with Visual Studio 8.
Oct 10, 2007 at 8:13 PM
I uploaded a simple prototype demonstrating module-coupling for you to take a look at. There's really nothing there as far as implemented logic, it just shows one possible way to tie everything together. Alternately, we could go with dynamic assembly loading, but that's something we would need to decide as being an important feature. The VS2005 solution is in the build/ directory.

My main goal is to get a solid design together, then we can work on actually implementing things. Until then, any new work will probably require a rewrite to fit into the finalized framework.

Just let me know what you like/dislike about the module coupling prototype, and we can go from there.
Oct 11, 2007 at 3:49 AM
I added some input logic to demonstrate how the event/message system could work.
Coordinator
Oct 11, 2007 at 3:50 AM
I like the look of it so far, just a couple of questions.

1.) Why in 2005 pro instead of express? It will be difficult to test the engine without the content pipeline at this point. As an engine I think we should use both. Things loaded at runtime from a gui, like from a file > open > filename, are nice for an engine, but loading from the pipeline is good as well, for teaching XNA to others, and for custom model processors using the pipeline. I think having in 2005 pro is nice for when we hit XNA 2.0, but moving from express to pro would be a breeze once XNA 2.0 comes out anyway.

2.) Why use 'float dt' in the update of things like the input subsystem instead of gameTime? Is this because you'd have to pass the gameTime from component to component? I think it'd be nice to keep one kind of timing system throughout.

Oct 11, 2007 at 4:08 AM
Yeah, once we start building the real thing we should definitely include project files for both. Personally, I use VS Pro and only use Express if I have to. I think keeping the content in the IDE in just kind of messy and it usually ends up being rebuilt way too often. I just build the content off-line. http://www.codeplex.com/xnadevru/Wiki/View.aspx?title=XNA%20Content%20Builder%20%28XCB%29&referringTitle=Home You can just set up a project in XNA Content Builder, with references to your custom content processors, and then only rebuild when you actually change something. That being said, integration with the IDE is important for those who choose to use it.

The timing thing is purely a matter of preference. I think overall having a float is more useful, as 99% of the timing calculations will end up being in floating-point anyway, so you'll just end up with a lot of int -> float casts. If there's a compelling reason to use int, then fine.

Thanks for the feedback. I'm especially looking for feedback on the module binding, as that's going to form the backbone of the whole design.
Oct 11, 2007 at 4:16 PM
Regarding the message passing, do you think there should be one central message dispatcher, or have one per event category?

For instance, right now there is a single EngineMessageEvent event in the game class, with a single message structure and type enumeration. We can make this class general enough to handle any kind of inter-module message, but I'm concerned with efficiency. The graphics subsystem isn't going to care that the user moved the mouse, but in the current system it will still get all of the mouse move events. So I'm thinking that maybe we should split up the events, and have separate handlers for the different message types, like input, component interaction, etc.

What do you think?
Coordinator
Oct 12, 2007 at 5:24 AM
Looks pretty good so far. I know this is just a test, but would we have input handling in whatever need it? For instance right now the input is handled by the input system, but the effects are in 'game' only. Right now of course 'game' is the only thing using it because there is nothing else, so is it fair to assume that if there was 'game', 'player', and 'camera', that all 3 would check input for things they cared about? And if so, lets say the W key moved a player forward, but could also tilt a free roaming camera down, how would we decide which received input?

Also, I'm curious what if !XENON is? I seems like you're not checking keyboard/mouse for the 360, but I thought that was something like !XBOX360. I've never programmed for the 360 so I'm not really sure.

I agree about separate message types/handlers.

One way I've seen it done is that almost all communication between classes is done with messages, you pass all messages to a god class that knows about every system in the game, and then it passes the message to the appropriate destination(s). I've seen entire projects based on enumerated messages. I think that might be a little much for this engine though.

Really there are a lot of ways we could do it. Enumerations are usually pretty quick though, as they're simply an int. We could find a way to only pass the messages to the appropriate system. If the message isn't meant for the graphics subsystem then it never reaches it, and the graphics subsystem continues with whatever it normally does, unaffected. Although this is just off the top of my head, easier said than done, things might be different if I had a chance to actually code something at home.
Oct 12, 2007 at 7:23 PM
All of the classes derived from ISubSystem must implement an Initialize method, that takes a QuickStartGame reference. Hence, any subsystem can use that reference to add event handlers to EngineMessageEvent. Now things like Player and Camera are a bit different. They're not engine "subsystems," I would consider them to be like the "Components" in your template. As such, I think we should have a ComponentSubSystem that acts to manage the components, and provide a wrapper around the message system for them. Of course, that's off the top of my head so I haven't thought my way through that one yet.

At the end of the day, I envision a distinct difference between "subsystems" and "components." Namely, subsystems are more ethereal things that provide services (like rendering, audio, physics, etc.), where as components are the entities in the game world, both visible (players, physics actors, flying cows, etc.) and invisible (triggers, etc.). I'm still trying to envision exactly how all of the communication between subsystems and components will occur.

As you imply, XENON is Xbox. It's just a relic from my personal naming convention of using hardware names instead of platform names (i.e. X86 instead of WIN32, XENON instead of XBOX360, etc.)

Right now the whole message system is implemented using C# events, but there's no reason why we need to keep it that way. I think we should still categorize events, though. Having each subsystem manually subscribe to every possible event it may need would be a pain. We should try to define categories of events, and then each category has an enumeration of message types, ideally with unique IDs so subsystems can subscribe to "ranges" of enum IDs.
Coordinator
Oct 13, 2007 at 6:08 AM
Setting up a range was actually something I was thinking about as well. Give a decent range, like 0-100 for one category, 101-200 for another, etc...

Having some things as components is fine if it will make things easier and stay efficient. Going with components could eliminate the need for messages, but I agree with the earlier design idea of not requiring dependencies. We should also keep in mind making the code as easy to understand as possible, and should consider setting up a coding and commenting standard as well that all contributors could be required to follow.
Oct 15, 2007 at 8:20 PM
I updated the prototype. I added some very simple GUI elements that show off the event framework.
Coordinator
Oct 16, 2007 at 5:29 AM
Edited Oct 16, 2007 at 5:32 AM
I really like the layout of the design so far, I wish I had more time to look at it.

What would really help, at least for me, and probably for anyone planning to switch to the new framework (whenever it is released), is if you could release a simple tutorial. I released one with version 0.16, here's the link if you haven't seen it yet: http://www.nfostergames.com/XNAQuickStartEngineWalkthrough.htm

A simple example would probably suffice. For instance, you could add the terrain component we're currently using, to your framework, and show the necessary steps. This would be enough for someone to see the major differences between the current engine and new framework. This would also be a chance for you to show the benefits of the new subsystem layout.

Just some thoughts. Good work so far.

By the way, I've had a few emails from others that have said they'd like to help. I let them know if they're serious about helping to let me know, and if so we may see some more contributors on the project. We'll have to figure out how to manage it. I think we should fill the issue tracker with bugs and features whenever we think of them. If contributors aren't able to create new features we'll need at the very least they might help solve bugs. Let me know your take on this, if you have one.

EDIT: Oh, and if/when you choose to release a tutorial, we should definitely have the code commented. If I knew the base better and you weren't in the middle of changes I'd be helping you do that already.
Oct 16, 2007 at 3:25 PM
Yeah, I'll definitely do that once the design is finalized. At the moment, there are just too many things in flux. Though if there are others joining the team, I'll try to finalize at least the basics very soon.
Oct 22, 2007 at 8:29 PM

shawmishrak wrote:
All of the classes derived from ISubSystem must implement an Initialize method, that takes a QuickStartGame reference. Hence, any subsystem can use that reference to add event handlers to EngineMessageEvent. Now things like Player and Camera are a bit different. They're not engine "subsystems," I would consider them to be like the "Components" in your template. As such, I think we should have a ComponentSubSystem that acts to manage the components, and provide a wrapper around the message system for them. Of course, that's off the top of my head so I haven't thought my way through that one yet.

At the end of the day, I envision a distinct difference between "subsystems" and "components." Namely, subsystems are more ethereal things that provide services (like rendering, audio, physics, etc.), where as components are the entities in the game world, both visible (players, physics actors, flying cows, etc.) and invisible (triggers, etc.). I'm still trying to envision exactly how all of the communication between subsystems and components will occur.


Perhaps you could use the GameServices from the xna Game class to communicate between sub systems. iirc you can define an interface for each sum system and then register that with the game services. When a sub system wants access to another sub system, it can get a reference to it with a call like:

IAudioService audioService = (IAudioService)Game.GameServices.GetService(typeof(IAudioService)

audioService.SoundFinishedEvent += OnSoundFinished();
audioService.Play("Headshot");

It doesnt matter how the perticular sub system implements Play() as it is through an interface, but the GameServices keeps a nice location for each system to find others.
Oct 23, 2007 at 6:20 PM
I've thought about using the XNA framework. Our subsystems would need to be Services and Components, since they also require Update() to be called. I'm still not convinced that this is sufficient however, since the XNA Services framework is based on an synchronous model, whereas ours could very well end up asynchronous, especially in the physics processing. I don't disagree that the XNA model is flexible and worth investigating, I just haven't put that much thought into it yet.

Oct 23, 2007 at 7:16 PM
Using Service and Components makes perfect sense for the core game systems. But physics will (well I hope it will) run in seperate process and at specific intervals the physics data will be moved to the rendering thread and update the relevant entities.

So parts of the code should use Service and Component were as other parts will use a different mechanism, at least a possible implementation.
Oct 23, 2007 at 7:50 PM
I'm not sure you would need a seperate mechanism, as even systems that run asychronously may want to recieve an Update() call once per frame.

For example, for my physics engine, each time the game called ApplyForce() on a body it would be added to a total. At the end of the frame Update() is called for the physics, where it copies the value of these totals into its own internal variables. The physics then continues to use this force in its iterations until told otherwise by the next Update() call.

The main problem I can think of was that of communication between systems involving one running in its own thread. Maybe some sort of command buffer would be needed in such cases, but shawmishrak seems to have more experience about this than me :)
Oct 23, 2007 at 10:45 PM
Messaging and data transfer are the two primary concerns here. Any work done by a synchronous Update call to an asynchronous module would be limited to sending "requests" to the proper threads and not actually touching any data.

The XNA Services framework could be a viable way to implement the subsystems, I'm just not sure it's going to give us enough functionality to be able to regulate who has access to what. For instance, the scene manager should have direct access to the physics module (specifically, an interface to a physics module) to add/remove bodies, create constraints, add impulses according to game logic, and lock the position/orientation data of the bodies to update the local scene manager descriptions of the bodies in a consistent state. Yet, you don't want to give the audio subsystem the ability to pause the physics threads.

Oct 23, 2007 at 11:25 PM
Each system could have a/many MessageListener class. This class specifies which type of message it's going to listen to.
When a message is sent from one system, it goes to some central mesage router which looks at the message type and sends it to the listeners that have decided they want to recive that type of message.

When the MessageListener recieves a message, it could either save it for the module to pickup later when it is ready, or call a delegate immediately.
Oct 24, 2007 at 6:41 PM
With that route, I would just pump Update as a message type. That would eliminate the need for per-frame Update() calls. Just send a FrameUpdate message to each subsystem.
Oct 25, 2007 at 8:36 PM
We need to get an overall design done and documented soon, as no one can really do any work on the engine until we have certain standards decided on.

  • What exactly constitutes a sub-system, and a component
  • How do sub-systems communicate
  • How do sub-systems and components update and/or draw
  • How are components and sub-systems going to be managed. Are they going to be held by our own version of the xna game class?

  • What sub-systems are we going to initially have
  • Define the interfaces for these systems

Before we get this formally decided, it's difficult for any of us to do any proper work on the project.
Oct 25, 2007 at 9:58 PM
I definitely agree. Unfortunately, I haven't had much time to work on things lately. I'll try to sum up your questions.

  • A sub-system is a logic unit of the engine. It takes care of a certain type of processing, i.e. graphics, audio, input, physics, scene management, etc. A component is an entity in the world, i.e. camera, player, bunny, etc.
  • I'm still working out the details, but sub-systems should communicate via messages, in general. i.e. Input system sends events to whatever subsystems need to act on the events. Now, there are exceptions to this. Namely, the interaction between physics and scene management. Sending messages for every position update would just be too inefficient. I'm thinking the scene manager will just hold on to a reference to the physics processor.
  • We could have sub-systems receive Update messages once per frame. Or, we could just implement an Update call in each sub-system. I'm really not sure at the moment which way would be better. Components on the other hand are handled exclusively by the scene manager. The scene manager implementation will be free to arrange and draw the scene nodes (components) however it sees fit based on the spatial partitioning, etc.
  • Sub-systems are managed directly by the Game wrapper (QuickStartGame in the prototype). Whether or not it will utilize the XNA Services framework, I don't know. Again, components are managed by the scene manager.
  • At the very least, we need graphics, GUI, input, and scene management sub-systems. Physics and audio are important, but not strictly required. If there's anything you feel is important, just say so.
  • Explicitly defining the interfaces is definitely something we need to do. The ones in the prototype are really just placeholders at the moment, as I haven't had the time to work them out completely.

If I get some time, I'll try to work up a preliminary document.

Oct 26, 2007 at 1:14 AM
Edited Oct 26, 2007 at 1:15 AM
Aphid/Sturm (and LordIkon),

Like I've said before, feel free to play with the prototype and come up designs as to how you feel things should be implemented/integrated. So far, the prototype has been my sandbox, but that's just because there was no-one else on the project working on the new stuff. As much as I want to see this project succeed, I don't have the time to be the chief architect and design the entire system. I'll contribute what I can and express my opinions, but I can't be the only one writing the code. My background is primarily graphics and high-performance systems, and I'm sure there are good software engineering principles that I am missing. I originally signed onto this project as the physics system developer, and that's still my main goal in the project, but a good solid base is required before that work is started.

All I'm trying to say is don't think you're limited to just posting suggestions on these boards. Feel free to implement things in the code and produce demos of how you think the systems should work.
Coordinator
Oct 26, 2007 at 2:01 AM
I was waiting for the prototype framework to finish to begin integrating the engine. I've been continuing work on increasing the framerate and look of the game. I figure this engine won't succeed without a good framework or continuing improvements and additions to keep the community interested, I just happen to be working on the latter.

I realize the prototype won't be "finished" all of the way any time soon, but I need it at a point where I have all the components ready for the current components to be throw in. When that time comes, just give me the go ahead.
Oct 27, 2007 at 3:07 PM
I had a few ideas on how I think some things could be implemented, but instead of trying to explain it all I decided to code it. I wrote most of this before shaw made his latest update, and quite a few of his implementations are better than mine, but I thought I should upload this anyway.

I built input and audio subsystems, but while doing this I decided that there were no advantages to using messages over direct method calls for these systems.

There were a few subtle differences to shaws prototype, like sub systems recive an Update(TimeSpan elapsedTime) instead of Update(float dt) call, and the value of this timespan is either gameTime.ElapsedRealTime or gameTime.ElapsedGameTime, depending on weather or not QuickstartGame.LockFramerate is true.

Sub systems recive an Update(), Draw(), and Initialize() call when appropriate from the SubSystemManager, instead of them being sent though messages. The order than systems recive Update() and Draw() is dependant on their int UpdateDrawOrder property.

I could modify the audio system to go with shawmishrak's version, as we don't have any audio yet, even if we use none of the rest of this code :)

http://www.filefactory.com/file/bf187e/

p.s. Is there anywhere on this site where I can upload files are are not patches?
Oct 27, 2007 at 4:24 PM
It looks like clean code, good job; just a few comments:

The purpose behind the message passing was to modularize the code, and provide an intuitive approach to inter-module communication. I can see how audio may be directly tied into another system and may not gain much from the message passing scheme, but input was a prime candidate for it. It just seems more intuitive to receive events when the state changes than having to query for every possible input each frame.

I'm not sure about SubSystem.Draw(). I'll have to think about that some more.

I think what should happen now is have everyone look at both source trees, and pick out the parts of each that you favor. We all need to be on the same page here, so its important for everyone to voice their opinions. Then, we can take the majority-favored parts of each source tree, combine them, and create the new base code for the QuickStart engine.

What do I mean by "parts of each?" I mean the structure of the engine. For instance, do you prefer the message-passing or direct method invocation model? Do you prefer input events, or direct input queries? Basically, any piece of the engine where you like one of the two implementations over the other, or even if you have a third way. We're at a point now where we can start finalizing this design, so I want to make sure everyone agrees with it. I don't want to force anything on anyone without giving them the ability to comment.
Oct 27, 2007 at 6:43 PM
I thought about setting up events for the input system, but didn't get round to it :)

I didn't want to use messages for the input, as it seemed a lot easier to say something like "input.OnKeyPressEvent += OnInput();" than setting up a message and filtering through them to find input messages.

Also, currently the messages carry around redundant data a lot of the time, such as the dt and axis fields, that only do anything useful for a few types of messages.
Oct 27, 2007 at 7:04 PM
The messaging system could be relatively easily refactored to have one or more event end-points per sub-system, defined in the interface.

Also, the idea behind including all possible fields in the one message type was to prevent run-time allocations. At run-time, we will pool messages to prevent allocations, but if we keep an "object" reference in the message type that is re-instantiated each time the message is sent, you will end up with needless allocations. It's actually more efficient to pass along small, useless bits of data than cause several run-time allocations per frame.

If the consensus is to change to the multi-event based messaging system, I'll go ahead and refactor that. I need to rename all of the class variables anyway to match the coding guidelines document.
Oct 27, 2007 at 11:52 PM
There are architectual issues to consider using the different methods mentioned here, which I would like to discuss before we start implementing the final messaging system. The direct method invocation has some disadvantages:
  1. You will have to pull each supported action from the server, this means a lot of unneeded queries as there might not be any change from one update to the next. I.e. input is a example of this as pressing keys with 1/60's of a sec isn't all to likely.
  2. There is a direct relationship to the bound subsystems interface. So changing the interface of a sub system can have a major impact on the rest of the system, even if the external behavior hasn't changed.

Using the messaging also has some issues (I'm refering to the code for 1.77, so this might have changed, shaw comments?)
  1. Using events to pass information to other systems has the draw back that it can happen async. So the sub systems get notification as soon at something happens. This isn't usually a good idea as updates to a game state shouldn't be done outside Update or during Draw. (This can be fixed by moving the invokation inside the main update method)
  2. Invoking a general event like EngineMessage often means that a lot of listeners are just filtering out a lot of noise and not doing anything, besides if the fix from above were used listeners would do this during update, which would lead to issues.
  3. The order of updating is not garentueed by the donNet framework, and this might lead to issues as I could imagine that some subsystems would require update before others.
  4. Since there isn't any real eventing logic in events raising the same message multiple times between updates will simply invoke the same listeners multiple times. Mouse movement might serve as an example here, if the user moves the mouse, and pauses and moves the mouse again (all within one cycle) the two two messages will be fired (at least) though only one is needed, as only the combined distance is of interest.

Of cause the above issues are solveable using events, I think that most dotNet users would find the resulting event story difficult to understand.

One way of solving most of these issues would be to implement a message queue which the different sub systems could query for during update.

Also a note, I'm coming from a C++ background and back then using #ifdef was simply just something that I did. Today using C# I mostly see #ifdefs as design flaws. I can see that in both source trees this is used, I strongly believe that we can do without these. In the imput example these could simply be removed if we were to use a input manager which would load the input handlers specified in the game configuration.
Oct 28, 2007 at 2:09 AM
Note: I am currently engrossed in the OSU v Penn State game, so it's entirely possible my comments below may not make sense :)

While using a message queue can give you a guarantee that messages will only be processed in the Update loop, it also constrains you in the opposite way: Messages sent are guaranteed to experience a latency of one frame before being processed, assuming a message sent in one frame is put in the next frame's queue. You could also use one queue per sub-system and allow an early-updated sub-system to send a message and have a later-updated sub-system receive the message in the same update frame, but then you run into consistency issues. A single message can be received by one sub-system in one frame, and by another sub-system in the next frame. This may or may not be an issue by itself, but a frame-lag in the input system could be noticeable by users.

How would you envision the message queue being implemented? It seems like a local-queuing scheme might work a little better. Messages could be directly sent to sub-systems by events, but sub-systems are free to just queue the messages internally and flush the queue whenever it so chooses (within the Update method).

Personally, I like preprocessor directives like #ifdef. I actually wish C# had a macro processor. It is true that many cases of #ifdef in C# can be avoided with virtual calls and modular code, and that's the "correct" way to handle it in the general case. However, in some cases, avoiding the virtual call is well-worth the "ugliness" of #ifdef.

Coordinator
Oct 28, 2007 at 2:44 AM
Edited Oct 28, 2007 at 2:46 AM
The only alternatives to #ifdefs are 1.) Create two seperate engines, although very similar, one will be for the 360, the other for the PC, or 2.) Remove all windows specific code from the engine, which means losing the mouse and keyboard capabilities among other things.

Edit: ^^ or virtual calls like you just said....
Oct 28, 2007 at 3:06 AM
Edited Oct 28, 2007 at 3:10 AM
Imagine you're writing a platform-optimized function. You have three options:

  1. #ifdef the code, one for PC, one for Xbox PPC, one for PS3 Cell, etc.
  2. Call through an interface, incurring a virtual call penalty (on consoles, this can be as bad as a multiple cache miss plus a complete pipeline stall)
  3. Write multiple implementations, then decide at compile-time with a typedef.

Clearly option 3 is out if you're using C#. Option 2 is feasible if the improvement from the optimization is much greater than the virtual call cost. The only problem with option 1 is potential "code ugliness."

Edit: Of course, this isn't as big of an issue with C# as it is with C++ since C# doesn't allow you to do as much optimization (i.e. SIMD is out), but its still an important consideration.
Oct 28, 2007 at 3:40 AM
Basically, I'm thinking of the following, along with the current messaging framework:

public void MySubSystem.HandleEvent(EngineEvent evt)
{
lock(m_messageQueue)
{
m_messageQueue.Push(evt);
}
}

public void MySubSystem.Update()
{
// Some processing

EngineEvent evt;
for(int i = 0; i < m_messageQueue.Count; ++i)
{
lock(m_messageQueue)
{
evt = m_messageQueue.Peek();
m_messageQueue.Pop();
}
// Process message
}
}


Sorry about the formatting, this forum leaves a lot to be desired.
Oct 28, 2007 at 4:00 AM
Well the problem isn't "code ugliness", it's code complexity. You wouldn't need to create a totally new engine. Only the parts that have #ifdef need to be factored out. If you add another target platform the paths throught the same file become event more complex, this makes the code very difficult to understand (though VS helps by graying out non compiled areas).

While I do agree that virtualization adds a overhead to the run-time invocation (which might be worse on the console, I don't know as I'm not used to code on consoles), I really doupt that we are in a state where this kind of optimization gives us more that i.e. code path analysis, and shader optimization.

If virtual invocation is so bad on the console, I think we have another problem as the engine is build around virtualization.
Oct 28, 2007 at 4:22 AM
Oh, I'm not saying we're at that stage, I'm just saying preprocessor directives have their uses. In an ideal world, the compiler would be able to auto-parallelize code, using both multi-core optimizations and SIMD optimizations, but not even C++ compilers can do this efficiently yet, let alone C#/.NET.

To be honest, the fact that the engine is built almost entirely around virtual classes does concern me, especially on Xbox, but I think keeping inter-module calls to a minimum should limit the effects. What you need to watch out for is making hundreds of virtuals calls per frame. If you only call virtual Update/Draw calls, it's not a huge deal. The example I gave a couple posts ago applies to code that is called many times per frame. Basically, keep virtual calls out of inner processing loops and we should be good.
Oct 28, 2007 at 4:49 AM
Everything is build around virtualization if we want to or not. If our code is having performance issues which have only be solved by limiting the number of virtual calls, I think we should look towards the Xna team and get them to optimize their code as well, as the whole framework uses virtualization.

In regards to your query post I think that would be the behavior for almost all sub systems. And almost all still need to filter out the needed messages I would image something simpler like this (Use { { and } } without spaces to format code):
public void MySubSystem.Update()
{
    // We never expect more the X (10) number of events for MySubSystem per update call
    List<EngineEvent> events = new List<EngineEvent>(10);
 
    events.AddRange(this.Game.GetMessages("MyMessageIdentifierA"));
    events.AddRange(this.Game.GetMessages("MyMessageIdentifierB"));
 
    foreach (EngineMessage message in events)
    {
        // Do something
    }
}

(I think you mean i <= messageQueue.Count as otherwise you will not get the last element ;))
Oct 28, 2007 at 5:19 AM
What parts of the XNA Framework are you referring to? Client code (and the disassembled XNA code I've seen) deals almost exclusively with derived types, not virtual base types.

In theory, that code segment is nice, but I would like to avoid the IEnumerable interface (i.e. events.AddRange() and foreach on an IEnumerable type). It has the tendency to produce a fair amount of garbage.

Actually, now that I look at my code closely (I was paying a lot more attention to the OSU game at the time, lol), the loop is all wrong. It'd be right if Count was cached, but it really should be while(m_messageQueue.Count > 0).
Oct 28, 2007 at 5:34 AM
I might be confused here but can you explain the diff between a base of a derived type and a virtual base type?

In theory yes you are right that IEnumerable can produce a lot of garbage, but you can just create your own type and do it yourself :)

Of cause you can always just loose the foreach all together and do
public void MySubSystem.Update()
{
    // We never expect more the X (10) number of events for MySubSystem per update call
    Queue<EngineEvent> messages = new Queue<EngineEvent>(10);
 
    this.Game.GetMessages("MyMessageIdentifierA", messages);
    this.Game.GetMessages("MyMessageIdentifierB", messages);
 
    while (messages.Count > 0)
    {
        EngineEvent event = messages.Dequeue();
        // Do something
    }
}
Oct 28, 2007 at 6:22 AM
shawmishrak, if you are going to update the code the support the coding guidelines would you update the "interfaces" as well? They aren't interfaces but abstract classes. I did it in a local build which resulted in
    public interface ISubSystem : IUpdateable
 
        /// <summary>
        /// Initializes the sub system
        /// </summary>
        /// <param name="game">The <see cref="QuickStartGame"/> the sub system is attached to</param>
        void Initialize(QuickStartGame game);
 
        /// <summary>
        /// Indicates that the system has been initialized
        /// </summary>
        bool Initialized
        {
            get;
        }
 

Then I created a base SubSystem class (interface only here):
    /// <summary>
    /// Base class for all Sub Systems
    /// </summary>
    public class SubSystem : ISubSystem, IComparable<SubSystem>
    {
        public bool Initialized
        {            get         }
 
        /// <summary>
        /// Gets the current game the system is attached to
        /// </summary>
        public QuickStartGame Game
        {            get        }
 
        /// <summary>
        /// Gets or sets whereever the system is enabled
        /// </summary>
        public bool Enabled
        {
            get
            set 
            {
                this.OnEnabledChanged();
            }
        }
 
        /// <summary>
        /// Gets or sets the order of update for this sub system
        /// </summary>
        public int UpdateOrder
        {
            get 
            set
            {
                this.OnUpdateOrderChanged();
            }
        }
 
        /// <summary>
        /// This event is raised when the enable state changes
        /// </summary>
        public event EventHandler EnabledChanged;
 
        /// <summary>
        /// This event is raised when the update order changes
        /// </summary>
        public event EventHandler UpdateOrderChanged;
 
        /// <summary>
        /// Compares this sub system with another
        /// </summary>
        /// <param name="other"><see cref="SybSystem"/> to compare to</param>
        /// <returns>0 if they have the same sort order</returns>
        public int CompareTo(SubSystem other)
 
        /// <summary>
        /// This initializes the sub system
        /// </summary>
        /// <param name="game">The <see cref="QuickStartGame"/> the sub system should be attached to</param>
        /// <exception cref="InvalidOperationException">If the sub system has already been initialized</exception>
        /// <exception cref="ArgumentNullException">If <paramref name="game"/> is null</exception>
        public void Initialize(QuickStartGame game)
        {
            this.InitializeCore();
        }
 
        /// <summary>
        /// This method is invoked when the system is being initialized
        /// </summary>
        protected virtual void InitializeCore()
        {
        }
 
        /// <summary>
        /// This raises the <see cref="EnabledChanged"/> event
        /// </summary>
        protected virtual void OnEnabledChanged()
        {
            this.EnabledChanged(this, EventArgs.Empty);
        }
 
        /// <summary>
        /// This raises the <see cref="UpdateOrderChanged"/> event
        /// </summary>
        protected virtual void OnUpdateOrderChanged()
        {
            this.UpdateOrderChanged(this, EventArgs.Empty);
        }
 
        /// <summary>
        /// This updates the sub system
        /// </summary>
        /// <param name="gameTime"><paramref name="gameTime"/> contains information on the current cycles game time</param>
        public virtual void Update(GameTime gameTime)
        {
        }
    }
The subsystem interfaces just had:
    public interface IInputSubSystem : ISubSystem
And the final Sub System Implementation would have:
    public class InputSubSystem : SubSystem, IInputSubSystem
The reason for using the SubSystem base class was simply to encapsulate the common code (event trigger, compare) I used compare because I played around with changing update order, you prob want to omit that ,)
Oct 28, 2007 at 6:25 AM
What I'm referring to is that when you work with the XNA Framework, you are using derived types directly. You don't work through interfaces or abstract base classes, and you are calling non-virtual methods. For example, you use a GraphicsDevice directly, not through an IGraphicsDevice interface. Since the methods are not virtual, you do not incur the virtual call cost.

Look through the XNA code in Reflector. How many virtual methods do you see? :)

Also, with you messaging code, you do realize you only eliminate the overhead of filtering, and not the code complexity of it, right? In the sub-system code, you still need to switch based on the message type.

What are your thoughts on the one-frame message latency in that system?
Coordinator
Oct 28, 2007 at 6:35 AM
I've found one-frame message latency systems to work perfectly when implemented right. However, for messaging systems in games/systems that including network support there should be a way to send a message that will not be expected to occur in one frame, and will simply occur whenever it gets to where it is going. Messages themselves should also have information in them that describes if it is the type of message that is going to everyone on the network, just the client, or just the server. These are just off the top of my head.
Oct 28, 2007 at 7:00 AM
I want to hide the complexity from the user. Using the messaging system you could write something like this:
public void MySubSystem.Update()
{
    // We never expect more the X (10) number of events for MySubSystem per update call
    Queue<EngineEvent> messages = new Queue<EngineEvent>(10);
 
    this.Game.GetMessages("MyMessageIdentifierA", messages);
    while (messages.Count > 0)
    {
        EngineEvent event = messages.Dequeue();
        // Do something
    }
 
    this.Game.GetMessages("MyMessageIdentifierB", messages);
    while (messages.Count > 0)
    {
        EngineEvent event = messages.Dequeue();
        // Do something
    }
}

Yes the user would still have to filter on inner state like this:
public void MySubSystem.Update()
{
    // We never expect more the X (10) number of events for MySubSystem per update call
    Queue<EngineEvent> messages = new Queue<EngineEvent>(10);
 
    this.Game.GetMessages("KeyDown", messages);
    while (messages.Count > 0)
    {
        EngineEvent event = messages.Dequeue();
        KeyEventArgs args = event.args.Convert<KeyEventArgs>();
        if (args.Key == Keys.Q)
        {   ....
        }
    }
    ....

In the above example you would only get one key down message no matter how many keys were pressed during the cycle. The idea behind this is that some messages can be merged into one message when raised. The Keys enumeration is a flag can you can therefore have any number of keys. So when calling the message registering system with a message, it would check if the event is mergeable and if it is then merge it with any existing message.

Is one cycle latency a issue? Hmmm, depends on the rest of the implementation. But for the game, no, because all other messages are also 1 cycle late. With regards to networking that's a different story, also all messages raised durring the current update call, should not be raised until the next cycle. So networkign shouldn't pose an issue in that regard.
Oct 28, 2007 at 7:45 AM
Your solution does seem pretty elegant, I'd be interested to see it in practice. Would you keep the string-based message types? Remember that strings are immutable in C#, and I'd be cautious about doing a lot of string compares.

I'm still not sold on the one-frame latency not affecting anything, but I guess we'll find out.
Coordinator
Oct 28, 2007 at 7:56 AM
Depends on what you mean by affecting. What kind of changes that occur over a single frame are you going to notice? Unless we have a system that will stop processing messages if a time limit on message processing for that frame is reached, then everything should occur almost instantly. Although it depends on the complexity of the messaging system (how long is takes a message to reach its destination).
Oct 28, 2007 at 8:02 AM
It depends on how much of total communication will occur with the message passing scheme. As long as we keep the "heavy-weight" communication out of the message system, like the scene manager <-> physics simulator link, it should be fine. However, a frame or two lag in mouse movements and key presses "may" become noticeable.
Oct 28, 2007 at 8:03 AM
Ideally network messages are 2 cycles behind in P2P as they are send from client1 in cycle 1 and recieved by client2 in cycle 1, but because it was recieved during update, it won't take affect until the next update cycle.

Now this looks differently when playing against a server as there it need one more cycle to process all current clients.

The amount of time it takes to take effect depend on a multitude of things (network latency, machine speed etc) and there is a need for some kind of sync between the client and server (or other clients)
Oct 28, 2007 at 8:19 AM
You can't compare network latencies to engine message latencies.
Oct 28, 2007 at 8:50 AM
I wasn't, I actually tried to point out these were two seperate issues.
Oct 28, 2007 at 10:07 PM
Ah, alright.

This does bring up an interesting point, though. Were you planning on building the engine around a client/server architecture?
Oct 28, 2007 at 11:18 PM
I think that the most sound way to build game engines today is to make the client/server all the time. Even when playing single player game, it's a client/server game running.
Oct 28, 2007 at 11:27 PM
Edited Oct 28, 2007 at 11:28 PM
Then we need to create an intimate binding between the messaging system and the network layer. We also need to implement two different network systems: one on the new XNA 2.0 Live library, and the other on System.Net. We cannot rely on Live for Windows, since its not distributed with the XNA redistributable.

Do you have any thoughts on how to implement the network layer(s)?
Oct 28, 2007 at 11:34 PM
I also think that the networking should bind in somewhere around the messaging system. I haven't given it any thought at this time though.

Yes we need to abstract the networking away as there are prob going to be many changes to the networking layer over the next few Xna releases.
Oct 29, 2007 at 5:29 PM
Some of the modules, like the graphics system will have more than one class (one for text, sprites, models, prostprocessing, etc.). Will all these different classes be accessed though the GraphicsSubSystem instance? Or are we just going to make the classes public?
Oct 29, 2007 at 6:50 PM
I haven't really given the graphics sub-system a lot of thought, but on a high-level you want everything to be accessed through the IGraphicsSubSystem interface, even if that only means passing an instance of IGraphicsSubSystem to the constructor. I think at first we can just defer everything dealing with text and sprites to the XNA framework. You can just use IGraphicsSubSystem.GetDevice() to get the underlying GraphicsDevice instance to pass to the constructors. For models/postprocessing, these would ideally be handled only by the graphics sub-system. The scene manager will maintain a render queue structure that tells the graphics sub-system how to render a frame.
Oct 29, 2007 at 7:55 PM
In my work for "Slash" command system I'm creating a controls hirachy which requires a "form" as the base container. It's similar to the Forms/Controls structure known from Windows.Forms/Asp.Net. Though not as deep nor complex :)

They are all public and are just created without any parameters, similar to those known hiracies. The GraphicsDevice can then be retrived by:
this.Device.Draw(...);
A control isn't rendered or updated before it's added to a screen, and until a control is added to a screen device will null, as all controls render using the same device, which is set on the screen.
Oct 29, 2007 at 11:29 PM
Hi Shaw, With regards to the messaging I made a mock up of what I would envision on the project. I did thatonly with respect to how fast it will be to retrieve the messages from the queue. My initial thought was to create a method which would have the type and the target queue, similar to above. I then tried different solutions where I could avoid using strings. I ended up with :
        Queue<IMessageType> allQueue = new Queue<IMessageType>();
        public void GetMessages<T>(Queue<T> messages) where T : class, IMessageType
        {
            foreach (IMessageType message in allQueue)
            {
                if (message is T)
                {
                    messages.Enqueue(message as T);
                }
            }
        }
Then I created a method which populated allQueue with 100000 messages ( two different types keyboard/mouse with them placed randomly in the list). On avarage there then is 27000 keyboard messages and it takes about 12ms (On a Lifebook laptop) to traverse. I feel this is good enough for the engine, as we will never have that amount of messages in the queue during a single cycle.
Oct 30, 2007 at 3:08 AM
That looks very nice in theory but is it really feasible to have sub-systems explicitly query for every message type they could possibly be interested in? I would highly recommend at least setting up categories.

Performance-wise, I see some potential problems:

  1. Like I've said before, we really need to stay away from IEnumerable. I know this is just a mock-up and there are other ways to iterate over the queue, but I can't stress enough the fact that foreach on collections should be avoided.
  2. There needs to be some way to pool messages to re-use them. Dynamically allocating all messages is feasible on Windows with a generational garbage collector, but that's going to nip us on the butt on the Xbox. That was the whole point of AllocateEngineEvent() in the prototype, all new messages come from one point, so we could implement a re-use pool.

If possible, can you send me your test code? I want to run it through the performance tools on Xbox to see what kind of GC foot-print that has. I'm not too concerned about the run-time type inference, but the possible GC activity on Xbox has me worried.
Oct 30, 2007 at 11:27 AM
You could sort the messages into queues for each catagory at the begining of each frame. Then when GetMessages<T>() is called, you just return the appropriate list.
Oct 30, 2007 at 11:31 AM
I did exactly that in the little test I've created, though I'm thinking about doing a better add, I need to find a better solution to that.
Oct 30, 2007 at 12:42 PM
Edited Oct 30, 2007 at 12:43 PM
As there seems to be a general interest in this I posted my sample code below. This is in no way the final implementation but it might spawn some ideas and maybe Shaw can use this to do profiling on the XBox. I didn't optimize the clering of the messages so there is an obvious performance optimization here. I didn't do it because (and partly because I'm at work) is that available.Values returns a collection and it's not possible to use indexers there, and since foreach is out of the question then I had to think of something else, but I haven't had the time for that yet. Maybe someone else has a good solution?

(In order to make it run simply create a new console application and pase the code, replacing the existing)

namespace ConsoleApplication4
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics;
 
    class Program
    {
        // This would be Update in then engine/sub system
        static void Main(string[] args)
        {
            #region Ignore Test code
            Engine engine = new Engine();
            Program.Populate(100000, engine);
 
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();
            #endregion Ignore Test code
 
            engine.BeginMessageHandling(); // this should be done in the update on the QuickStartEngine not the sub system
 
            ReadOnlyCollection<MouseMessage> messages = engine.GetMessages<MouseMessage>(); // Sub system requests messages from the server
            // TODO: Here the sub system would do what's needed inside update
 
            engine.EndMessageHandling(); // this should be done in the update on the QuickStartEngine not the sub system
 
            #region Ignore Test code
            stopwatch.Stop();
            Console.WriteLine(string.Format("Items: {0} - Time: {1}", messages.Count, stopwatch.ElapsedMilliseconds));
            #endregion Ignore Test code
        }
 
        #region Ignore Test code
        public static void Populate(int count, Engine engine)
        {
            Random rand = new Random();
            while (count-- > 0)
            {
                if (rand.Next(0, 10) <= 8)
                {
                    engine.AddMessage(new KeyboardMessage());
                }
                else
                {
                    engine.AddMessage(new MouseMessage());
                }
            }
        }
        #endregion Ignore Test code
 
    }
 
    interface IMessageType : IDisposable
    {
    }
 
    public class MessageType : IMessageType
    {
        ~MessageType()
        {
            this.Dispose(false);
        }
 
        public void Dispose()
        {
            this.Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        protected virtual void Dispose(bool disposing)
        {
        }
    }
 
    public class KeyboardMessage : MessageType
    {
    }
 
    public class MouseMessage : MessageType
    {
    }
 
    class Engine
    {
        private Dictionary<Type, List<IMessageType>> current = new Dictionary<Type, List<IMessageType>>();
        private Dictionary<Type, List<IMessageType>> available = new Dictionary<Type, List<IMessageType>>();
 
        public void BeginMessageHandling()
        {
            // This swaps queues as new messages shouldn't be processed until next update
            this.available = this.current;
            this.current = new Dictionary<Type, List<IMessageType>>();
        }
 
        public void EndMessageHandling()
        {
            // There might be an optimization here as you could loop through the collection and Dispose
            this.available.Clear();
        }
 
        public void AddMessage<T>(T message) where T : class, IMessageType
        {
            if (this.current.ContainsKey(message.GetType()) == false)
            {
                this.current.Add(message.GetType(), new List<IMessageType>());
            }
            else
            {
                this.current[message.GetType()].Add(message);
            }
        }
 
        public ReadOnlyCollection<T> GetMessages<T>() where T : class, IMessageType
        {
            if (this.available.ContainsKey(typeof(T)) == false)
            {
                return new ReadOnlyCollection<T>(new List<T>());
            }
 
            return new ReadOnlyCollection<T>(this.available[typeof(T)].ConvertAll<T>(new Converter<IMessageType,T>(delegate (IMessageType input) {
                return input as T;
            })));
        }
    }
}
Oct 31, 2007 at 1:01 AM
Alright, I had to modify the code a bit to make it run in real-time. I really just integrated it into a Game class, which called Populate every frame, followed by retrieving both kinds of messages. The results aren't too bad.

To summarize, with 5 messages per frame at 60 Hz, the garbage collector ran once every 20 seconds, with a 10 ms latency. Roughly 40kb of garbage is generated per second.

Messages/Frame    GC Freq.    GC Latency    Garbage/Sec
    5             20 sec      10 ms         40 kb
    10            19 sec      10 ms         55 kb
    20            13 sec      15 ms         79 kb 
    50             8 sec      27 ms         138 kb

I didn't go over 50, since it seems highly unlikely that we'd be pushing more than 50 messages per frame. The numbers for up to 10-20 messages seem acceptable, though I would highly recommend trying to minimize the amount of garbage being generated. The higher the GC latency, the more noticeable it will be. Remember the Xbox GC is non-generational. The GC runs after every 1 meg of allocation and freezes the system while it runs. The two things that jump out at me are the creation of lists for every frame and every call to get a new message type, and the anonymous delegate in GetMessages() (anonymous delegates are throw-away code and cause garbage). I would recommend trying to re-use the lists and remove the anonymous delegate. Since the messaging system is going to be the heart of the engine inter-connect, I want to make sure its as efficient as possible.

On a side note, I hope you don't feel like I'm deliberately trying to be a hard-ass about this. I just want to make sure its efficient as possible, since its such a critical system. :)
Coordinator
Oct 31, 2007 at 1:12 AM
This is exactly the type of testing we'll need once the prototype is being integrated with the existing components. I like it. I also like the fact we have someone on the team that has an Xbox360, and experience using XNA with it. I will likely be getting an Xbox360 around Christmas, but that is 2 months before I can even start learning how to use XNA with it.
Oct 31, 2007 at 1:32 AM
I'm looking forward to Christmas for the XNA 2.0 release. I can finally do away with C# express once and for all!

I just wish the compact framework team would release information on what they're doing for 2.0. It doesn't really seem like they listen to community requests like the XNA Framework team does.
Coordinator
Oct 31, 2007 at 1:45 AM
I am looking very forward to it. I was really hoping for windows form support, but that can be done manually I guess. I am hoping that the support for occlusion culling is easy to implement. That will be nice with large scenes. I wonder if my terrain will be able to occlude things....like a large mountain blocking everything behind it that you couldn't see, all of those things could be occluded.

It will be Microsoft's Christmas present to the community, and in turn I put cough up money for their console.....and pray that Ninja Gaiden II comes out asap. :)
Oct 31, 2007 at 2:08 AM
Edited Oct 31, 2007 at 2:13 AM
Honestly, Xbox support is one of the only reasons I use XNA. If there was no Xbox support, or students were allowed access to the XDK, I would just stick with native C++ code.

But I can't pass up the opportunity to play around with the Xbox hardware, lol.
Oct 31, 2007 at 5:21 AM
Shaw if you were to dispose the messages in EndMessageHandling, instead og just clearing the list and let GC take over, how would it look like then?
Oct 31, 2007 at 1:17 PM
I took a real good look at the engine last night.. there is a lot of this i could actually use so im glad i did,
i did get the feeling however that its being written from the mindset of a FPS player.. was this intentional, the game im getting ready to write will be in the style of an rts.. so the camera would work totally different and some other things probably wouldnt even be required for my game. i foud if i turned some stuff off it broke the way things looked. my main gripe though was the camera implementation. am i just missing something ? or should i put an RTS style camera in and upload the patch ?? if it was intentional then could other style of gameplay be considered while writing the new api ? ill be more than happy to be the RTS to make sure things dont go off in a direction that doesnt help me. :) in general i was happy with what i looked at.. especially the terrain stuff, iv been looking for a decent quadtree implementation for a while now.. im working on making this deformable within the XNA window so if this is something that you would also like i woill also hapiily contribute this once i finish
Oct 31, 2007 at 1:27 PM
Apologies for any duplicate posts, i dont know what is going on with codeplex today but everything is throwing erros but in some cases saving anyway...
im not sure how to delete the dup posts though.
Oct 31, 2007 at 1:29 PM
Edited Oct 31, 2007 at 1:30 PM
For input/questions about camera please use this thread Camera. But I guess it's safe to say that Camera will change for the new framework.
Coordinator
Oct 31, 2007 at 2:21 PM
I am in the process of adding new camera functionality for v0.178, if you could tell me what you mean by RTS camera I could try and accomodate for it. For instance, if you're talking about Civ4, the camera strafes over a map, always facing a specific angle, but can zoom in and out. Currently the FreeCamera can do all of these things, the strafe however could be put into a function, right now you strafe the camera with A and D keys. Zoom is done with the right mouse key.

The engine was made with FPS in mind to begin with because the engine is designed with the community in mind. Most the of the 3D XNA games/ideas I've seen or heard of seem to involve a 1st/3rd person perspective. However, the engine is going to be as flexible as possible, and won't be limited to FPS games. An RTS game like Civ4 will likely be possible. We will have the ability to have an in-game cursor and GUI.

Like I said, I'm working on making the camera more useful and friendly, if you would like to contribute I would love to see deformable terrain.
Oct 31, 2007 at 3:22 PM
Yeah by RTS i mean just like you explain.. the straffing i could only get to work left and right.... id like to be able to go north south east west using arrow keys or whatever you specify.. zoom with mouse wheel again configurable... and then the ability to go into free look if the use presses alt for example..

im really wanting deformable terrain for myself so hopefully ill get that working soon :)
yeah sorry the camera stuff wasnt in the camera discussion but i felt in general the post belonged here...
Oct 31, 2007 at 3:50 PM

Sturm wrote:
Shaw if you were to dispose the messages in EndMessageHandling, instead og just clearing the list and let GC take over, how would it look like then?


I'll take a look at it tonight, but my guess is that it won't have much of an effect, if any. The IDisposable interface can give you semi-deterministic resource clean-up, but as far as I know it still doesn't let you explicitly delete objects like the "delete" keyword in C++ does. The GC is still free to choose when to cleanup and delete the object. Honestly, that's not even the problem here. Really, IDisposable is for unmanaged resource clean-up that the GC would not know how to handle, like pointers/handles to native types.

In the end, it's not going to help with the dynamic memory foot-print, and that's the key here. The GC is going to run on Xbox once for every 1024 kb allocated in the managed heap, so we need to reduce dynamic allocations, not insert determinism into the process. That's why I'm recommending that we eliminate the dynamic list allocations and just keep single instances around with pre-set sizes. That will help a bit by reducing the dynamic creation of lists. Then, if we could find some way to recycle messages, that would be great. Something along the lines of keeping around a few instances of each message type, then recycling them in EndMessageHandling() each frame.

Isn't non-generational garbage collection fun? :(
Coordinator
Oct 31, 2007 at 8:21 PM
I say we get Microsoft to design a new Xbox that fits our GC needs.
Oct 31, 2007 at 9:54 PM
Another possibility would be to create a MessageItemManager which would be responsible for allocating/releasing MessageItems, this way we would save the constant garbage collection, though it would require a bit more mem to be allocated initially.
Oct 31, 2007 at 9:58 PM

LordIkon wrote:
I say we get Microsoft to design a new Xbox that fits our GC needs.


It's not the Xbox hardware, it's the implementation of the Compact Framework.


Sturm wrote:
Another possibility would be to create a MessageItemManager which would be responsible for allocating/releasing MessageItems, this way we would save the constant garbage collection, though it would require a bit more mem to be allocated initially.


Yeah, pooling is usually the recommended approach when trying to reduce dynamic allocations. In this case, I think it's reasonable to have a little extra up-front allocation to prevent garbage through-out the game execution.
Nov 1, 2007 at 2:07 AM
Disposing the messages in EndMessageHandling() decreases the GC latency by a bit, but the GC still runs just as often. So, there is a bit of a gain, but not too much. We need to find some way to prevent per-frame allocations, including lists and messages.
Nov 1, 2007 at 2:13 AM
Also, as is, these collections aren't really an issue. A GC latency of 10 ms probably isn't going to be too noticeable, but I just want to make sure we have room to grow and not use all of our GC latency allotment in one place.
Nov 1, 2007 at 10:29 AM
Would it be worthwhile having a class diagram of what you currently have in the form of the new API? the test code thats in the source at the moment, is that a good indication of the way you're going or have things changed dramatically? if not i can easily create class diagrams on the projects that are already there. would definatly make it easier to see progress for new comers especially.
Nov 1, 2007 at 12:43 PM
The current QUickStart code will change quite a bit. You can see this change by getting the latest sour and apply patch 422. There are more interface changes on the way, messaging system, input, controls etc.