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).

29 Apr 2007

Nebula2 support in Nebula3 + status update

I'm currently working on the low-level graphics subsystem, resource management and basic scene rendering code. I plan to do the next SDK release as soon as the Tiger tank from the Toolkit examples will render.

A few info tidbits until then:
  • Resource setup will be decoupled from the actual resource objects through resource loaders. This was an optional mechanism in Nebula2, but is now the only way to setup a resource object. This modular approach allows more freedom for 3rd parties to extend the Nebula resource management and makes the code cleaner and easier to understand (its a similar concept to Streams and StreamReaders/StreamWriters)
  • Asynchronous loading for all graphical resources will be supported right from the beginning.
  • Nebula2 binary file formats (nvx2, nax2, n2) will be supported directly through legacy loader classes. It is possible to remove legacy file format support by changing a #define to reduce the code footprint. This should make migration to Nebula3 much simpler, since Nebula2 asset tools can be used to create resources for Nebula3 in the beginning.
  • There is now a clear separation of per-model data (shared across all instances of a model), and per-instance data. Per-instance shader attributes must now be explicitly requested by the application. This allows the rendering code to perform more optimizations under the hood for instance rendering, and it makes the rendering code much simpler.
  • Access to per-instance data is much simpler, more intuitive and much more efficient (no more nRenderContexts).
  • Most per-instance data now persists between frames, and doesn't have to be recomputed every time an instance is rendered (which may be up to multiple times per frame). Nebula2 did this only when absolutely necessary (particle systems and skinned characters). Nebula3 increases the per-instance memory footprint slightly (which is bad), but simplifies the rendering code a lot, and reduces per-instance state switch overhead drastically (which is good).

24 Apr 2007

Back To XP...

Oh well... I was just installing Visual Studio SP1 over the network and then after 10 minutes of absolutely nothing happening suddenly the screen went black and it was like beeppppeeebbeeeeppeeeepppppp... well not quite, but the machine froze for several minutes and didn't come back. Trying to re-animate with Ctrl-Alt-Del just made things worse, not even the task manager came up. Maybe I'll give it another try after adding more RAM. Other then that it becomes apparent that Vista has some serious problems when heavy file or network stuff is going on. How they could fuckup something fundamental as this is beyond me. Back to my trusty Dell and XP for now...

New Machine

We got two shiny new machines from AMD yesterday (actually, they are huge, black and very ugly). One of them is now under my desk. For some reason there's only 1GB RAM in it (thanks AMD), so I know I'll have to plug in a couple of gigs more. The more interesting fact is the operating system: Vista Home Premium, 64 Bit Edition. I resisted the instant urge to reformat and install XP immediately, instead I'll actually give Vista try.

First impressions after a few hours of use:
  • Vista installation works great. The only thing I had to type in was the user name and password.
  • Booting takes quite a bit longer then my Dell box. Could be the BIOS though.
  • Vista integrated immediately into our company network without setting anything manually. Internet works, Samba shares are visible. Cool but a bit scary.
  • Firefox installs and works just fine.
  • TortoiseCVS works, but only with the latest release candidates.
  • Tcl install is a bloody mess. Some files could be written, some don't. Guess I'll have to retry with admin rights or something...
  • The entire control panel area is a big letdown. After 2 clicks you're back in those horrible tab-ridden, fixed-size dialog boxes from XP. Just more of them with even more tabs. Shame shame shame...
  • UI feels nice and smooth as long as no heavy file system work is going on. Uninstalling some pre-installed shit, and unzipping the DirectX SDK at the same time freezes the entire UI (including mouse pointer) for up to 20 seconds. Never happened to me under XP. Horrible.
  • I hate those new display setting panels from ATI which take forever to start (NVIDIA's as well btw.). What was wrong with the old panels?
  • Whoever came up with that UAC crap should be forced to install applications day in day out, with the occasional malware thrown inbetween. After 4 or 5 times, you just don't recognize it anymore, you just click it away. It's just like EULAs, just skip the damn thing and click Next. Looks like MS replaced their usability people with lawyers. I guess you can't sue MS anymore after actively agreeing to install a virus on your machine.
  • Still have to install VStudio and Maya... wish me luck.
All in all, as a user I'm very unimpressed with Vista so far. Many things have changed, but only some of them to the better. It feels more like a new XP service pack with a custom skin. MS says it spent as much money on Vista as NASA on the moon program, and all we got are translucent, blurry window borders, what a waste. I know there are quite a few technical improvements under the hood, like virtualization of graphics memory, new driver model and so on. But nothing of this internal stuff makes my life as a user any better.

2 Apr 2007

Yet another Gamer Card...

I finally found out the one and only proper way to display gamer cards and I feel stupid now. It's as easy as linking to http://gamercard.xbox.com/flohofwoe.card. They should really advertise this somewhere on the main page. Less bells and whistles then the mygamercard.net version, but I like it better, because it's simpler but still gives some useful info on mouse-over. I spent most of the weekend playing Dynasty Warriors 5 - Empires as you can see. I didn't expect so much fun from the game, as most reviews trashed it really badly. But I saw it for 30 Euro at my local game shop and gave it a try. It's true, the graphics would look shitty even on the old Xbox, and the game-play is repetitive. But for some reason it is damn addictive. Not quite Crackdown-addictive, but still playing-til-6a.m.-addictive. It plays like a bizarre mixture of Powermonger, Z and Ninja Gaiden. Ok, maybe Powermonger is a bit far out, but it *could* be a next-gen Powermonger if it wanted to dammit. Now that would be great.

1 Apr 2007

New Gamer Card

Just replaced the code which displays my gamer card on the left with something from mygamercard.net. The previous one was a simple image, and it seemed to be broken since the recent Xbox Live maintenance (probably because of this). The new one has more functionality but requires JavaScript and Flash. Not sure yet if I like it though. I run Firefox with the NoScript-Addon, and I absolutely despise Flash (at least if an entire web site is implemented in it).