18 May 2007

April NPD numbers, ouch...

Just saw the NPD hardware numbers for April. Not only did the PS3 fall under 100k in April (which few dared to predict), it went down to 82k! For giggles, that's even less than the Game Boy Advance (84k). On the other hand, the 360 didn't do very well either, 174k is clearly sub-standard. Nintendo rules the world as usual for this generation, selling more Wiis and DS (and GBA's!) then all of the competition combined.

Of Orbs And True Skills

Finally! I found my last agility orb in Crackdown. I played through the game again to check out the downloadable content, cleaning up the remaining gang members after killing the final boss. And there it is: the long forgotten green glow! It sits on a roof of a twin-tower skyscaper near the river in the Shai Gen district at a rather exposed location (must have missed it at several times in the last days). Like an alpinist his next mountain, I analyzed the building. The facade couldn't be reached from the street level, and there was only a row of window ledges along the 2 towers. But there was a building nearby which seemed near enough for a jump. Reaching the orb was easier then it looked like at first. Finally, I stepped into the green light, absorbed all the tiny little orbs, and then the sweet, sweet sound of "Achievement unlocked". Pure gaming bliss :) Crackdown is really one of those rare gems, where you pick up the controller and within 10 seconds you're totally sucked into the game and then you suddenly realize it's 3 hours later.

Speaking of suck... I also tried out the Halo 3 beta. The game plays great, but the first few rounds I totally got my ass handed to me by some Halo veterans. But then the magic of the TrueSkill system kicked in, and after the game realized that I totally suck at Halo it handed me down the ladder until I found sessions with equally bad players. I even won one session. Now I still think that I'm a pretty good FPS player once I'm warmed up. I spent countless hours in Battlefield 2 on the PC, but on the console, I still don't feel comfortable with those old-school shooters which don't provide a cover system. To me, the advent of cover systems has been the big turning point when console shooters finally became playable. I absolutely adore Rainbow Six Vegas for its cover system. It's ways above Gears' and GRAW's. I tried to play Quake4 and Prey on the 360 and I gave up after a few hours. And I must admit that I never really understood the hype around Halo. The graphics always looked meh when compared to other shooters of that time. I really (really) tried to like it by starting to play both Halo1 and Halo2. I gave up the single player campaigns in disgust after about an hour. So I was pleasently surprised when I looked into the Halo 3 beta. The game play is extremly smooth, controls are intuitive, graphics looked pretty good (but far from great).

Forza 2 demo: I've put countless hours into GT4 on the PS2, so Forza2 will be a must-have. I really like the time penalty when crashing into other cars. Makes Gran Turismo almost look like a arcade game. I used to crash into other cars in GT at strategic positions quite a lot to drive them off the track. Makes absolutely no sense in Forza. What I really didn't like was the lighting of the demo track. Lighting is why GT on the PS2 looks so real despite the low-poly environment. Forza just looks like a typical computer game. And I think it's the lighting. I can't put my finger on it, but there seems to be too little contrast and dynamic range. I know from our projects at Radon Labs that the wrong lighting can even make fantastic models look meh. In reverse, you can take last-gen models and characters, and with the right lights and post effects it will look breath-taking. I also tried out the driving wheel with the Forza demo, and while it's seems to be supported perfectly I still prefer the game pad controls.

Another favourite this week was that new pinball game on Live Arcade. Great stuff. Brings back memories of Pinball Fantasies on the Amiga :)

Hmm... busy gaming week this was...

9 May 2007

Dashboard Update!

The VGA level fix in the 360 dashboard update did wonders to the picture quality for my setup. Before the update I had the choice to connect my 360 to my TV (Sony) either through component or through VGA. Component has beautiful colors but fine detail wasn't perfect (there were slight artifacts around characters for instance). The VGA picture is extremely smooth and detailed, but had washed out colors on my TV. Also: component doesn't upscale DVDs, so the TV switches back to PAL (576p or so) and does the "upscaling" itself (which really looks crappy on my TV). DVD playback through VGA nicely upscales to 720 or 1080 (the latter doesn't make sense with my TV since it only has a 720 panel). So before the update I could either have nice colors, but slight artifacts, or a perfectly smooth picture with poor colors. The dashboard update fixes the VGA colors, and upscaled DVDs look perfect now (better then my old upscaling DVD player with HDMI I'd say). No reason to get that Elite now for its HDMI output :) Oh and the other new stuff is pretty cool too I guess, switching blades feels more responsive now, and it's now easier to find stuff on the marketplace.

5 May 2007

Templated Methods

I wasn't aware that one can do something like this in C++:

Ptr< MyClass > myObj = factory->Create< MyClass >(MyClass::RTTI);

I only saw this in some XNA code examples, and took it for something that's specific to C# generics. Turns out it works in C++ as well. When working with smart pointers, this is pretty nice, because automatic pointer casting doesn't work between smart pointers of different types. So the alternative to the above code would be (assuming the Create() method returns a Ptr<>):

Ptr< MyClass > myObj = (MyClass*) factory->Create().get();

Pretty ugly with the required cast and the .get() method...

In some cases, templated method calls may also save an implicit hidden construction of a smart pointer object. I'm not completely sold yet however:

The bad:
  • it seems to be an unusual construct, haven't seen this yet in C++ code, maybe some compilers choke on this
  • templated methods MUST be inline, and cannot be virtual, so it maybe that the mechanism cannot be used everywhere where it would make sense
On the other hand:
  • it's a perfect fit for smart pointers
  • it may produce more efficient code (and should never produce less efficient code)
I'll have to ponder over this for a little while... I feel that the underlying problem is that a smart pointer doesn't provide a cast-operator to a different (compatible) pointer type... maybe with the newfound wisdom there is a way to add a templated cast operator to the smart pointer class itself, which would make the raw pointer casts and calling the .get() method unnecessary, but this would still require implicit object construction at method calls in some cases.

Nonetheless C++ never stops to amaze me, in some sort of scary way...

3 May 2007

The Nebula3 Resource Subsystem

More info on the Resource subsystem. Since this is work in progress, details like class names may change. Generally speaking, the Nebula3 resource subsystem is more open, and gives the programmer much more control over how resources are created and managed as compared to N2.

Nebula3 Resources have the following properties:
  • wrap some sort of data required by other Nebula subsystems
  • can be shared by ResourceId
  • can be loaded (initialized) and unloaded at any time
  • can be loaded synchronously or asynchronously
Typical graphical resources are for instance Meshes and Textures, however the Resource subsystem is not limited to graphical resources.

The resource subsystem has 2 operational levels (maybe those will be placed into 2 different namespaces, at the moment they are both under namespace Resources):

The lower level provides actual resoure objects, handles resource sharing, loading and (less important) saving. The low level resource classes are:
  • ResourceId
  • Resource
  • ResourceLoader
  • ResourceSaver
  • SharedResourceServer.
The higher level of the resource subsystem provides resource management, which means loading or unloading resources dynamically based on some feedback from resource users. The high level resource subsystem classes are:
  • ResourceProxy (alternate class name: ManagedResource)
  • ResourceProxyServer (alternate class name: ResourceManager)
  • ResourceMapper
Here's how the resource subsystem classes actually work together:

A ResourceId is a unique identifier for a resource. The resource id is used for sharing, and for locating the resource data on disc (or wherever the data is stored). ResourceIds are string atoms. Atoms are unique 32-bit identifiers for constant strings (or other complex data types) which speed up copying and comparing alot, and also reduce the memory footprint since identical strings are stored only once. To locate the resource data on disc, ResourceIds usually resolve into a valid URL (a resource id may for instance look like "texture:materials/granite.dds", which would resolve into something like "file:///C:/Programme/[AppName]/export/textures/materials/granite.dds" at runtime.

A Resource object is the actual container for the resource data. Specific resource types like textures and meshes are subclasses of Resource with specialized class interfaces. Resource subclasses are often platform specific (for instance D3D9Texture), but are conditionally typedef'ed to platform-independent interfaces (for instance Texture). Unlike in Nebula2, resource objects do not know how to setup, load or save themselves. Instead, a suitable ResourceLoader and/or ResourceSaver object must be attached to the Resource object. Since Nebula applications rarely write out data, ResourceSavers exist more for completeness. On the other hand, ResourceLoaders are essential, since they are the only way to setup a Resource object for use. ResourceLoaders have total control over the resource setup process. They can be platform-specific, and may depend on an associated platformspecific Resource class. This gives the programmer much more control over the resource setup process compared to Nebula2. Example resource loader classes are StreamTextureLoader, StreamMeshLoader (setup textures and meshes from streams), MemoryVertexBufferLoader and MemoryIndexBufferLoader (setup vertex buffers and index buffer from data in memory).

The Resource class also provides a common interface for synchronous and asynchronous resource loading. Synchronous loading is done like this:
  1. res-> SetResourceId("tex:system/white.dds");
  2. res-> SetLoader(StreamTextureLoader::Create());
  3. res-> SetAsyncEnabled(false)
  4. res-> Load()
  5. if (res-> IsValid()) ... then resource loading was successful, otherwise the method LoadFailed() will return true.
Asynchronous resource loading is very similar:
  1. res->SetResourceId("tex:system/white.dds");
  2. res->SetLoader(StreamTextureLoader::Create());
  3. res->SetAsyncEnabled(true);
  4. res->Load();
  5. the resource will now go into pending state...
  6. as long as IsPending() returns true, repeatedly call Load()... of course a real application would do something useful in the meantime
  7. at some point in the future, after Load() is called, the state of the resource will either be Valid (resource is ready for use), Failed (loading the resource has failed) or Cancelled (the pending resource load has been cancelled)
An application or even the Nebula3 render code usually doesn't have to deal with this, since the resource management layer will take care of this and hide the details of asynchronous resource loading behind resource proxies.

The SharedResourceServer singleton offers resource sharing by ResourceId. Creating resources through the SharedResourceServer makes sure that a resource is only loaded exactly once into memory, regardless of the its client count. If the client count of a resource drops to zero, the resource is automatically unloaded (this is no substitute for proper resource management however, that's what the ResourceProxyServer cares about). Resource sharing can be bypassed completely by creating Resource objects directly with Nebula3's standard object creation mechanisms.

A ResourceProxy (or ManagedResource) is a resource management wrapper around an actual resource object. The idea is that the contained resource object may change under the control of a resource manager based on resource-usage-feedback. For instance, a texture proxy may provide a placeholder texture as long as the requested texture is background-loading, a lower resolution texture may be provided if all objects using the resource are very small on screen, the texture may be unloaded if it hasn't been rendered for X frames and so on...

The ResourceProxyServer (or ResourceManager) singleton is the frontend to the resource management system. It is the factory for ResourceProxies and associates ResourceMappers with Resource types, other then that it basically hands all work down to the attached ResourceMappers.

The ResourceMapper class is where the interesting stuff is happening. A ResourceMapper is associated with one resource type (e.g. Texture or Mesh) and attached to the ResourceProxyServer by the application. A ResourceMapper is responsible to load/unload resources based on resource usage feedback from the rendering code. Subclasses of ResourceMapper may implement different resource management strategies, and it should be possible to create completely customized, platform- and application-specific resource management scenarios by deriving specialized subclasses from ResourceMapper and probably ResourceLoader. The goal is of course, that Nebula3 provides a good set of ResourceMappers out of the box for simple cases (just load everything that is required) up to streaming scenarios for large worlds.

The resource usage feedback is written by the rendering code to ResourceProxy objects and should include stuff like whether the resource may be needed in the near future, whether the resource was visible at all, and a guesstimate of the screen space size of the object using the resource. However the specific feedback depends on the ResourceProxy subclass, there are no common feedback methods in the ResourceProxy class.

Based on the resource usage feedback, a ResourceMapper could implement the following operations (however that's completely up to the actual mapper):
  • Load: asynchronously load a resource at a specific level-of-detail (for instance skipping higher res texture mipmaps if not needed), provide a placeholder for res0urces that are still loading
  • Unload: completely unload a resource, freeing valuable memory
  • Upgrade: increase level-of-detail for an already loaded resource (for instance loading higher-resolution mipmap-levels of a texture)
  • Degrade: decrease level-of-detail for a loaded resource (for instance dropping higher-resolution mipmap levels of a texture)
That's it so far! I plan to do the first code drop with a very simple resource management (asynchronously load required resources, but don't care about on-screen-size or automatically unloading resources).