Intel sponsors gamedev.net search:   
Promit's VentspaceBy Promit      

Welcome to Ventspace! Most posts here are delayed copies of posts from the real Ventspace.

Saturday, March 8, 2008
I'm recycling a couple old posts from a different blog here in order to pad things out in between entries for my game.

Object Lifetime and Destruction in .NET

I've noticed lately that a lot of people seem to have a kind of vague perception of what happens to objects after you're finished using them in .NET. It’s kind of critical to understand exactly how the lifetime model works, because it’s much more complex than older languages like C++ and can have important implications for an application. The relevant part of the standard is in section 10.9 Automatic Memory Management. There are three steps that can happen at the end of the lifetime of an object in C#:

1) Disposal (if an IDisposable object)
2) Finalization (the "destructor")
3) Collection (end of physical lifetime)

Step 1 is a fully explicit – dare I say, design pattern – which we use to create deterministic logical (but not physical!) lifetimes for objects. There is no magic behind it (with the exception of the using statement); calls to Dispose are explicit and the runtime injects no special behavior. The common Dispose pattern is not mandatory and again has no magic behind it. If you look closely at this pattern, there are two interesting behaviors. First, the call to GC.SuppressFinalize, which prevents Step 2 from happening, and second, the fact that the finalizer will dispose the object.

Step 2 is the first bit of behind the scenes magic. The C# finalizer looks like a C++ destructor, but it doesn’t really behave like one. When an object is no longer accessible, it is marked for finalization. Then another separate, low priority finalizer thread goes through all of the objects marked for finalization, calling their finalizers. An object can be revived by its own finalizer if it makes itself accessible again*. The memory of the object has not been affected in any way at this point. Also keep in mind that finalization, unlike disposal, is completely non deterministic and is dependent on what the underlying runtime decides to do. Particularly important is the fact that what order classes are finalized in is not specified -- and as a result, it is not safe to access any of your reference type members from the finalizer, since they may have already been finalized.
* An object which revives itself during finalization is still marked as finalized, and the finalizer will not be called again when the object becomes inaccessible again. Use GC.ReRegisterForFinalize to make the runtime call the finalizer again.

Step 3 is where the memory of the object is finally destroyed and released back to the OS. This is where the pointer to the top of the managed heap is moved internally and all that fun stuff. An object becomes eligible for collection if, at the end of its finalizer, it is still not accessible by any code. If the finalizer revived the object by making it accessible, the object will not be collected.

With regards to Dispose, the common Dispose pattern generally does all of the things that the finalizer would do, so there is no point in calling the finalizer; the GC.SuppressFinalize call that generally appears there is largely a performance optimization. Similarly, the invocation of Dispose(bool) from the finalizer is to avoid a memory leak in the case that an object was not properly disposed. Generally speaking, the difference between an explicit Dispose and a finalize is that the Dispose call will propagate the Disposal to any members which implement IDisposable, but the finalizer will not Dispose its members.

One question still remains -- when does all of this happen? Disposal is explicit, and I've mentioned that an object becomes a candidate for finalization when it is no longer accessible. The general assumption is that an object is no longer accessible when all references to it have fallen out of scope. It turns out that this is not entirely accurate. Take the following excerpt from the standard:

Note: Implementations might choose to analyze code to determine which references to an object can be used in the future. For instance, if a local variable that is in scope is the only existing reference to an object, but that local variable is never referred to in any possible continuation of execution from the current execution point in the procedure, an implementation might (but is not required to) treat the object as no longer in use.

What this basically says is that an object can be finalized as soon as it can't be used anymore, regardless of whether or not it is still in scope. Consider this code:
void ReadFile()
{
    Foo bar = new Foo();

    //other code that does not use bar
    int i = 0;
    ++i;
}

In this case, it is entirely legitimate for the runtime to finalize bar immediately, before the code using i is reached. Not only that, the MS CLR does in fact exercise this option if it wants to. If you're doing something important in your finalizer for Foo, and relying on the assumption that the finalizer will not be called until at least the end of the method, your code will break.

Hopefully that will help clear up a lot of the confusion about what exactly happens to an object before and after death. Calling any of these steps a "destructor" is a flawed perspective and does not accurately reflect the underlying behaviors. (Slight inaccuracy: The specs do in fact refer to the finalizer as a "destructor". I don't like this choice of terminology.)

Comments: 2 - Leave a Comment

Link



Thursday, March 6, 2008
I would really appreciate comments on this one.

The Concept -- Background and Overview

Historically, my hobby work has been focused on middleware and libraries. Game code was sort of incidental to the whole thing. I've written two games to date; both of these games were for school projects. They were also extremely small and simple, being more proof of concept than true games. (Although conveniently, both were pretty fun.) Of particular note is that in both of these cases, the game concept was not mine. I made tweaks, and I certainly appreciated the simplicity and cleverness of the concepts -- but I was never the originator or main designer. I mostly just provided advice on what I could and couldn't build technology for. Even at work, I'm just building someone else's game.

I don't like this state of affairs.

I'm an engineer, and I love it. Messing with engines, designing classes and interfaces and subsystems -- all of that is great. But those challenges exist in all of software development, and although they very slightly, they're still there. I got into game development to make games. And it's not that I lost sight of that; I've been painfully aware of it in my productive-yet-not 8 years of working on this stuff. The problem isn't that I don't want to make games. It's that I'm just not good at it. My ideas are typically thinly veiled ripoffs of games I played recently or just really enjoyed. Original ideas were totally lacking. Hell, I don't even have any original twists on existing ideas. I can't make an FPS with a single twist or combination of twists that makes it different from the other games out there. And I refuse to make clones.

On top of all that, i don't have the art skills or resources to build the games I really want to build. So I'm stuck using stock assets pulled off the web, which are painfully generic. I made some attempts to find an artist/designer to help me out, but that was a miserable failure as well. Designers in general tend to have some issues with understanding scope limitations and tech limitations (this is deliberate to some extent), and going to amateur designers made the problem much, much worse. All I got were ludicrously grand ideas that completely missed the point of what I was trying to accomplish.

Imagine my surprise, then, when my mind actually managed to generate an idea that was both original and doable on my own.

Unfortunately, it's a difficult idea to solidify. Extremely difficult. Its existence in my mind is more the evocation of moods and senses than real gameplay. I can tell you how the player is supposed to feel and think while playing. I'm not sure I can tell you just what the hell it is they're supposed to DO while playing. This particular journal entry is more for my sake than yours; I'm hoping that by forcing the concept into the vulgarities of written language, I can transform it from its rather abstract state in my mind to something concrete and implementable.

There's another part to all this. I've been a musician for a long time, and I love composing music. I'm not very good at that part. I've done some work, but I've only ever managed to produce two complete original songs. (Starting to see a trend here...) If you want to listen to them, they're online:
* Battle of the Apocalypse
* Hoarder's Legend
It's been a year two years since I wrote that last one. I have been really interested in doing more work lately, this time with live guitar work. Unfortunately, I've been seriously lacking in ideas, unable to extend any of the small themes I've thought of into anything substantial. That's always been a problem for me; I am not sure what it was that allowed me to build those two tracks. This problem has been reflected even in my guitar work, which is nothing but a pale reflection of popular rock and metal.

Once again, imagine my surprise when, at much the same time, a coherent and original musical vision emerged in my head.

I've been playing a game recently called Every Extend Extra Extreme (E4). It's from the same people who did Rez, and shares similar themes. I picked up an idea from this game that I've been somewhat obsessed with lately: procedural, reactive music. Half the idea is quite commonplace; the music reflects what the player is doing, giving them audio cues and setting the mood. The other half is a bit more exotic. The idea is that you don't simply play some kind of level up chime or start a battle theme or something. Instead, the music is dynamically built up to reflect the player's progress. Things start off slow and simple, the game and the music both. A little while later, things are in full swing, reflecting your actual game status.

I'm not building a game like E4 or Rez, but they are without a doubt the genesis of my concept, and I'm taking cues from them in what I'm doing. That core idea of reactive procedural music is key. Not to the gameplay itself, which is totally independent of the music. (The music affects the game in E4. I'm not interested in doing that, because the design and technical challenges involved become somewhat more complex.) The game could trivially be played with the sound muted, and it wouldn't to external appearances be any different. But the music is still critical, because it is the defining quality that sets the feel of the game, and that's where the inspiration from Rez and E4 comes into play. The music isn't part of the game; it's part of the experience.

In my case, I want the music to feed off the game much, much more heavily than in Rez or E4. In those games, the music is essentially just looped trance built on a set of layers which are turned on as the game progresses, and sped up in the case of E4. I don't have a problem with that; it's simple, effective, and catchy. I just don't think that it takes it nearly as far as it could go. So what I'm working on is truly dynamic, reactive music that is built up, taken apart, and put together again differently in seemingly intelligent patterns. I say "seemingly" because the real rules for the construction of the music are going to be quite simple and straightforward. But by assembling simple pre-recorded fragments using a simple set of rules, I'm hoping to see emergent behavior that results in a virtual DJ, like you'd get in a proper club playing house/trance. Note that I'm still using samples -- full blown procedural synthesis is awesome, but it's not the goal here. By being clever about the rules governing the system and the composition of the microtracks that it controls, very simple logic should be able to create very sophisticated results.

So then, there are really two separate but closely related concepts here. One is the game itself; the other is the music. Both deserve their own entries, and I prefer not to mangle them here. Both are, after all, quite nebulous -- I only wrote this entry to give those entries some context. The topic of emergent behavior is key to both visions. You can start to understand the vision set forth for the game by reading about Boids, if you're not already aware of it. Written by Craigs Reynolds in 1986, it's probably the most well known and classic example of emergent behavior in an AI computer system. The game is fundamentally Boids, but taken several steps farther. As for the musical vision, it's all hidden in Hoarder's Legend, although I suspect it won't be clear what that means until I elaborate. That track is the model for the development of the new music. Although the new stuff seeks a much different feel, far more intense and vibrant, the fundamental structure derives from Hoarder's Legend, and it's that structure that allows me to create the music as a complex, sophisticated whole even though it's nothing more than a machine following some simple rules. (Which raises some awkward questions about my abilities as a composer. Oh well.)

So that's a rough overview of how I got to where I am right now, and where it is I'm trying to go. There's still a lot to talk about with both the game and the music, since I haven't really explained either one. I don't know how long this project will take, but I'm planning to rapid prototype as soon as the basic concepts have solidified. I'll probably make that prototype available when it's ready. Not "if" it's ready. I absolutely believe this will work, and I will not give up on this one so easily. It's been a long time since I had a creative project to be excited about.

Comments: 3 - Leave a Comment

Link



Monday, March 3, 2008
Emo Reznor


"Sylvester Stallone" Reznor


"Tom Cruise" Reznor


"Michael Jackson" Reznor


"Linkin Park" Reznor (reference photo)


"Kurt Cobain" Reznor


"VTEC Just Kicked In, Yo" Reznor


"Fell in the Pool" Reznor


"This Is Sparta" Reznor


Comments: 2 - Leave a Comment

Link



Saturday, March 1, 2008
I just fixed a heap corruption bug in SlimDX. Most people find this type of error to be really annoying to debug and repair. I rather enjoy it, though. Same goes for stack corruption and other types of native code induced bugs.

On a side note, if you haven't noticed the XNA news from GDC, you are really out of the loop. Those guys have got some really cool stuff in the pipeline, especially now that they've settled on a sane distribution model. Zune support should be fun too.

The Evolution of SlimDX

There's been a lot of internal redesign in SlimDX over the last two months. I have to admit that most of these changes were driven by jpetrie and MikePopoloski; my involvement with the project was rather minimal for a long period in the middle. That's life I guess. The result of these changes is evident throughout the internals of the library, and unfortunately throughout SlimDX based code as well. Of particular annoyance is that we changed the semantic model used for managing object lifetimes. But I'll get to that a bit later.

I happened to be looking at the very early revisions of SlimDX yesterday in Google's repository viewer. (They got a new one. It's really nice.) It shouldn't be a surprise that there's no resemblance. The design focus of SlimDX has mutated over the period of about a year. I wrote the initial prototype in January 2007, shortly after completing a C#/MDX game for a course. The goal at that point was very simple: I wanted to get off MDX. In order to accomplish that, I wrote the new library very quickly, starting from a blank slate and filling in functionality until the game was functional again. (This is the only time I've ever used a pure TDD approach to writing software.) There wasn't any work done over the course of the next several months; I simply did some cleanup before revealing the code to other people in May.

The result of this approach was that the original SlimDX was a direct MDX clone, cut down heavily but otherwise identical. Features I never used, such as device events or Get* functions, didn't made it in. There still wasn't a specific design goal at this point, just a push to add more functionality to the prototype. That continued up until the August release, which was substantially filled out but otherwise had the exact same model as my initial draft.

After that, the userbase began to expand significantly, and their complaints plus the complaints of jpetrie and Mike began to drive change. Many of those design changes were driven by the inclusion of new subsystems, especially Direct3D 10 but DirectInput to a lesser extent as well. These architectural revisions were still mainly internal, and most of them didn't leak out much, except in the form of name changes or namespace changes.

We're coming up on the March release now, and the changes are far, far more widespread. Several steps have been taken in order to guarantee that the SlimDX architecture is consistent and maintainable long term. We're getting tired of changing fundamentals, and I think we're pretty close to the final structure of the library. We've added a Conventions page to serve as a development guideline, although that's still being worked on as well. The changes we've made should survive long term, and significantly enhance usability of the library. Unfortunately, they're also breaking changes at the semantic level. The object lifetime management semantics are the biggest change in this regard.

The problem, as jpetrie puts it, is that SlimDX has to play nice on both sides of the fence. Both COM and .NET have to be satisfied as much as possible with what we're doing. We can never screw up the COM side, because if we do we are at risk of leaking memory, trashing the heap, and other similar bugs. On the other hand, if we don't adhere to .NET's conventions, developers are likely to make mistakes that also result in memory leaks. (But that's about the worst that can happen, assuming we don't screw up.)

I'm not going to document the old and new models, as I don't feel this is a good place for it. It's relevant to all SlimDX developers, not just the ones who keep an eye on this space. jpetrie does a reasonable job of summing up the problem in his post anyway. This page sums up the rationale that led to the new implementation, as well. The short version is that now, we simply create new references to existing SlimDX objects whenever possible, rather than continually spawning new SlimDX objects. It's fairly obvious that this is a much better idea.

At the end of it all, our new model still isn't perfect. There are still awkward corners and special cases that will create non-intuitive behavior. I think this is unavoidable no matter what we do; the native and managed worlds are too different for the interaction between the two to be perfect. I feel that the model we've settled on is the idea, because it best serves the needs of the majority of people using DirectX. It's considerably more forgiving than either the old SlimDX model or the MDX model, and because we integrate leak tracking (which has been juiced up, by the way), it's very easy to track down the places where something goes wrong. I'll even go so far as to say that in a number of ways, we're even easier to use than XNA.

Comments: 0 - Leave a Comment

Link


All times are ET (US)

 
S
M
T
W
T
F
S
2
4
5
7
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

OPTIONS
Track this Journal

 RSS 

ARCHIVES
October, 2009
September, 2009
August, 2009
July, 2009
June, 2009
October, 2008
June, 2008
May, 2008
April, 2008
March, 2008
February, 2008
January, 2008
December, 2007
November, 2007
October, 2007
September, 2007
August, 2007
July, 2007
June, 2007
May, 2007
February, 2007