2015/06/29

Content Management: Design Questions

I spent the better part of the second week writing three blog posts about content management. I'm not going to post them. The amount of useful code I got out of them is exactly zero. It was kind of fun writing them, and there is still some useful info in there, so let me give you a brief rundown. Read this as a cautionary tale.

The Holy Grail

outlined the features I would expect from an ideal content management system. I also described an architecture I would use to build such a system from scratch. I immediately conceded that I would not do that, though, because the content manager in MonoGame works fine.
Essentially, I would love to unify pretty much every file access the engine has to make into the content manager: configuration, localization, asset loading, and save games.
Config and save files, I figured, should be both read and written with this system. If you treat strings like assets, I thought, supporting localization gets you a long way towards full blown mod support (or the other way around, depending on your point of view). Naturally, the actual file accesses should happen in a background thread while the main thread could safely use loaded assets. Finally, to save memory, I decided I would also allow selective unloading of content. The XNA and MonoGame content managers only allow unloading of all content, after all.

It's certainly possible to build all that on top of MonoGame, but nothing in life is without compromises, and I ended up nowhere near total unification. If you have to compromise on every feature, what's the point in covering each in detail?

Journey to the Center of the Code, Part 1

I really should have done my research at this point. I can't be the first person to want this stuff in all of the combined history of XNA and MonoGame, right? Well, past-me didn't care, and decided to start reading the MG source code, writing this post in parallel.
I learned some really cool stuff about the undocumented implementation details of the ContentManager and how it's used in the Game class, too. But I made a premature judgement about the raw asset feature I discovered in the process, which like the last third of the post and half of the next one are based on.

Those implementation details aren't really relevant to what I'll be doing, though (when that statement inevitably comes back to bite me in the ass, I'll explain the relevant parts only). The argumentation that follows them has a false premise and the project planning after that is based on it, so that's also pointless.

Journey to the Center of the Code, Part 2

I'm not even sure why I named it that, but in this post there's some more faulty project planning, and a much more insightful piece on selective unloading of content. The latter, thankfully, was not compromised by my lack of research, as I knew from prior projects that you're supposed to use multiple instances of ContentManager for this. If you have trouble trusting my word for this, the game state management code sample also does this.
Anyway, the interesting bit is a list of different systems to manage multiple instances of ContentManager and what type of game they work for best. Despite all that, however, I was still indecisive about what system to use, because I'm still not quite sure what specific kind of game I'll be building.

Once we get to partial unloading, I may get back to this. For now, the question is how to proceed.

The Plan

is to deal with configuration and save games first, since they will be treated separately in code. We'll start with an abstraction above the ConfigurationManager to make a type safe Configuration class that can easily be saved back to the app.config file.
For save games, there is the Microsoft.Xna.Storage namespace I never even knew about. We'll play a bit with that later on.
And then we can revisit parallelization and selective unloading. Localization will use the content pipeline, and we'll cover it once everything else is in place.

2015/06/25

Integrating System.Diagnostics: Instrumenting

With the support systems in place, it's time to instrument the rest of the code base: Program.cs and MainGame.cs. Not a lot of work, but that's the point: By doing it now and working with the requirement of instrumentation from this early on reduces the workload associated with it.

Program.Main()

There's two things we need to do here. The first is marking the beginning and end of the session (since all sessions log to the same log file, making the break obvious is a definite necessity). I originally wanted to make a blank line in the log file, but that's not straightforward enough for my tastes (in fact, I didn't get it to work at all) and I opted for simple Start and Stop events instead.
At this point, the Main method looked like this:
Padding the messages to 128 and 129 characters respectively ensures that they have the same total length (for pure visual appeal), and padding with a dash makes these entries highly visible, which I think is really important, since they are the only thing separating different sessions.

The other thing is crash reports. If an exception manages to slip through all other layers, I want to catch it here, log everything about the circumstance it occurred under that I can get my handy on, then quit the game as gracefully as possible.
To do that, I surrounded game.run() with a try/catch block and logged the exception like so:
This still isn't perfect, obviously, because there's probably more info I could get, and because it doesn't actually quit the application gracefully.
For the former, I will implement a ReportCrash() method on MainGame that logs as much of the game state as possible (though it would be empty at this point, since there is no game state yet), for the latter, I'll simply show a message box notifying the player that the game unexpectedly crashed and give them the option to mail the log file to me.
Note that I simply rethrow the exception when debugging, which makes sense because I'm still the only person actually possessing the "game" and I might be able to get even more info from the debugger than the log. The other features I didn't implement yet because I'm basically always in DEBUG anyway and emailing the log file to me from my own machine doesn't make a lot of sense.

MainGame

is much simpler to instrument, since it matches the use case for the rest of the code base and was intended to be simpler to do. I just pass in the TracerCollection, initialize a Profiler and a TraceSource, call Profiler.BeginFrame in Update and Profiler.EndFrame in Draw, and surround the rest of the methods with Profiler.BeginOperation and EndOperation. If that was too convoluted to understand, it looks like this:
As you can see, these support systems are really simple stuff. Of course there's always more stuff you could add, but I think this is a good balance of ease-of-use and feature count.

With all that boring stuff out of the way, the next up is content management, and how it relates to user config, save games, modding, localization and, weirdly enough, game state management.

2015/06/21

Integrating System.Diagnostics: Profiling

Before we get into profiling, I have one quick thing to note: Assumptions are bad. I knew there was something called Performance Counters in System.Diagnostics, and kind of assumed that I'd use them for my profiler, but as it turns out, they are a Windows thing for measuring your programs performance concerning memory, exceptions and JIT compilation. I might have a use for that, but the important part of the profiler, measuring time, will use a simple System.Diagnostics.Stopwatch after all.

Now, if you've been a programmer for any significant amount of time, you've probably heard this quote by Donald Knuth before:
Premature optimization is the root of all evil.
If you ever played a game that came out anytime in the last decade or so, you also know that performance in modern games is critical, so optimization will have to happen at some point. The trick to optimizing the right code at the right time is profiling. For those unfamiliar with the term, it means measuring how much time is spent executing different portions of your code.

There's different methods to do that, but I won't get into that here; I have no clue how I'd even implement a statistical profiler, so let's talk about why mainstream profilers like JetBrain's dotTrace and red-gate ANTS aren't particularly well suited to games. It all boils down to them being unaware of the game loop and the concept of frames.
Their profile reports primarily consist of a call graph, sorted by how much time each function cumulatively took. And that works fine if you're looking to triple your tech demo's average frame rate, but once different game states come into play and you're trying to even out performance spikes, all the valuable information is averaged out.
Modern profilers come with tools to alleviate similar problems, such as a histogram that allows viewing profile reports for subsections of the entire run, but that's still not enough: I want separate profile reports for individual frames, I want context information on the game state and what the individual functions were doing in that particular frame.
Essentially, I want my profile report to be interspersed with my log data. So I'll build a profiler on top of a TraceSource and a Stopwatch, and, once some actual optimization requires it, a log file viewer that is aware of the performance data inside, and/or a debug menu that allows stepping through individual frames while viewing their profile reports live.

The first draft

My first draft for this sort of profiler looks like this, but it leaves all of the heavy lifting to the hypothetical log viewer and does not allow live viewing of individual frame profile. It is also somewhat error prone in that there is basically no way to verify correct use of the BeginOperation and EndOperation methods. We can do better than that!

One way to ensure correct use, as suggested in Game Engine Architecture, is to create a class, say LogicalOperation, that calls BeginOperation in the constructor and EndOperation in the destructor, and create a local instance within the scope of the operation, and then to hide it all behind macros. That works in C++, since you know that all local variables are allocated on the stack, so you know exactly when their constructors and destructors are called.
In C#, however, where a local variable is allocated is not defined in the language spec, and thus is an implementation detail of the compiler. I considered counter-balancing this with IDisposable and using statements, but still, that's at least a couple dozen unnecessary allocations every frame, which will increase the frequency of GC induced lags.
Also, since we can't hide the local variable behind a macro, instrumenting the code would litter it with unused local variables, which would make static analysis a pain to use, and also masquerade the intent of the code to some extent.

Building a call tree

Fixing the other problem first, I decided, was more important, and would actually solve the second problem. If I extended a call tree in the calls to the begin and end methods, I could simply analyze that for completeness at the end of each frame, where I'd have to reset it anyway.
The problem with that, however, is that a straight object oriented tree would still cost many allocations a frame. You could pool them, of course, but I decided to do it simpler still and use a preordered array of nodes. The struct I used for the nodes looked like this at first:
and of course, there is no name for the logical operation yet. This is because it seemed somewhat hypocritical of me to use a string for that, when C# strings are immutable and thus needed to be reallocated every frame, which I'd been fighting against just before. I considered using a StringBuilder for that, which I will use for most persistent string buffers I might need in this project, but then I realized that I'd have to construct these strings for the log messages anyway, so that idea went straight out the window, and I added the field as a plain old string.
In total, this is what I ended up with. This still is not complete; it is not documented yet, there is no way to disable call tree construction (which is a necessity for live viewing, unless I end up copying out the array...), and I'd like to add some more info to the root node, such as current used memory and possibly a frame ID. But that's peanuts  to what we achieved already, so let's wrap this up here.

2015/06/17

Integrating System.Diagnostics: Logging

When debugging, before using an actual debugger, many programmers, myself included, use a method called printf debugging, after the function in the C standard library: You just print out a label identifying where in the program currently we currently are and possibly some of the program state.

When you do that professionally, it's called logging, and it is incredibly useful to determine which portion of your code probably contains the bug. Logging also implies that you do it more systematically, for instance by printing to a file. When you do that, you can also debug after the problem has already occurred, making reproduction of bugs a lot easier.

There's many logging frameworks out there, such as Apache log4net, but I'll be using the logging features of the .net framework, for two reasons:
  1. I'd rather not add dependencies to my game without a good reason; and, somewhat related
  2. since I'm already using MonoGame, I'd like to keep porting to platforms other than Windows as easy as possible. If I have to switch out my logging framework, that would make things harder.
Granted, there's a subset of log4net available that is compatible with both .NET and Mono, but both of these already come with System.Diagnostics, so why bother with subsets?

How does it work?

In principle, it's simple: there's this thing called a TraceSource, through which you append messages to your log. Before you can use one, you need to configure TraceListeners, which handle the messages and print them to different outputs, such as log files and the console. You also have to set up TraceFilters and TraceSwitches, which determine what messages get through to be actually logged.

Microsoft would like you to do that in App.config. Which isn't that bad an idea, really, it just doesn't suit my tastes really well, for a couple reasons:
  1. I really only want multiple sources because I want different log channels to filter console output by. I still want all their output in the exact same log file. The XML to do that in App.config is incredibly verbose and thus doesn't lend itself to quick changes.
  2. If two thematically related, but independent pieces of code should share the same channel, you either have to pass around the TraceSource object, introducing a dependency, or instantiate two of them. Neither sounds fun.
  3. I don't see myself using App.config for anything else, and chugging around a config file for something that is neither changed by the user nor updated after deploy seems stupid to me.
Since setting up TraceSources in code is almost worse than in App.config (albeit more flexible), and I still had to solve problem no. 2, I decided to write...

The TracerCollection class

I originally wanted to object pool the TraceSources here, but, as it turns out, there is no way to set the Name property after initialization, so I'd still be reallocating and initializing new instances, defeating the point of the pool.
Since I won't be instantiating more than, like, 8 different sources anyway, I decided to simply wrap a Dictionary<string, TraceSource> instead. Upon initialization, it silently creates the two listeners and, when a TraceSource is requested (by name, obviously) that is not in the dictionary, one is created and the listeners added.

So we get lazy initialization of TraceSources without any setup work required on the user side, just by passing around an instance of this class.
However, there's still some problems with this:
  1. You can't easily reconfigure what is logged to the console! At the moment, that involves changing the constructor of TracerCollection and recompiling. When we get to content management, we'll also deal with save games and user config, and then we'll also fix this.
  2. The TextWriterTraceListener isn't particularly smart. If the log file already exists, it just appends to it, making it hard to differentiate between independent runs of the game. I considered using timestamps in the file name, so as to make a new file every run, but that clutters up the directory really fast, so I'd rather put them into a sub directory. That's not an option either, because the listener doesn't take relative paths, and figuring out the absolute path of the running executable is a pain, so I'd rather just deal with concatenated logs.
Seeing as I don't have a solution for either, all that's left to do for now is instrumenting the code, but I'd rather do the profiling facilities first and instrument the little code I have with both in one step.

So next time, we'll take a look at System.Diagnostics profiling. See you there!

2015/06/14

Setting up the workspace

Turns out that isn't as easy as I expected. At least not when git is involved.

Setting up the project was the easy part, obviously. After I upgraded to the most recent versions of Visual Studio and the MonoGame SDK, I created a project and cleaned up the mess created by the project templates, removing faux-documentation from my code and renaming class Game1 : Game to the equally non-descriptive and much prettier MainGame. You can't really blame MonoGame for it, Microsoft's templates for XNA were equally messy.

Before I so much as built the project, I decided that would do for the initial commit. So it was time to set up git. I don't plan to make this project open source for a while, and I don't really want to pay github for a private repo, so I looked into hosting the origin locally.
Someone suggested hosting it via Dropbox, which I'm already using. What a great idea! I get to push stuff even when I'm offline (which I am for multiple hours a day) while also having access to my code from essentially anywhere. It's simple to set up, too! And I'm not being sarcastic here, I genuinely think this is a great idea. But if you've worked with VS and version control before, you probably know what I'm getting at...

When I started coding for the first blog post and built the project, I realized that I didn't commit the build output in \bin and \debug, so I committed just those for my second commit. After changing the code some more and building again, I realized my stupidity: were I to commit my code changes, I'd to also commit the changed executable. And since I'm building the project a lot during testing, I'd do that with every commit to come.

I don't know about you, but I think that's stupid. Since it would be like 10 minutes of work to start over and do it right, I decided to tear down what I had by this point.
I knew VS has version control tools integrated, and I expected them to be aware of a problem like that, so I looked into that next. I found this MSDN page, but the talk about team projects and hosting on either visualstudio.com or a local Team Foundation Server made me worry this was an embraced and enhanced version of git that I couldn't simply host on Dropbox.
Toying with the tools under that assumption proved me (mostly) wrong, however. From what I saw, VS seems to be able to work with any git repo in what's called the Team Window, but can't create a remote, even if it's hosted locally. Therefore, I assume, repos created by MS tools just come with appropriate .gitignore files, like this one by github.

To recap, here are the steps to setting up the dev environment exactly like mine right now, both for you and future me:
  1. Prepare for initial commit: set up the project, clean up template cruft, make sure it compiles
  2. Paste the appropriate .gitignore file in the project directory
  3. Open up the command line and navigate to the project
  4. Initialize a repo and commit:
git init
git add .
    git commit -m "initial commit"
  1. Navigate to the desired origin directory. Make sure the directory name ends in .git
  2. Initialize a bare repo:
git init --bare
  1. Navigate back to the project directory
  2. Set the origin and push your local changes:
git remote add origin E:\Dropbox\GitRepos\project.git
git push origin master
Obviously, you may need to adjust the path to your dropbox directory.

It's already been a learning experience for me, and I haven't even really programmed anything here. I guess this is what I get for never using version control in the five years I've been programming now.
Anyway, this post was probably pretty boring for the git gurus and all three git deniers out there. I'm afraid it'll stay this way for a little while, because I'll be a good citizen and spend my time integrating System.Diagnostics for Tracing and Profiling, so at least one two more posts of me summarizing MSDN and my own experiments...

Until then!