Twitter  YouTube  E-Mail  RSS
The One Man MMO Project
The story of a lone developer's quest to build an online world :: MMO programming, design, and industry commentary
The Blessing and Curse of Shared Pointers
By Robert Basler on 2012-03-23 23:44:12
Homepage: www.onemanmmo.com email:one at onemanmmo dot com

I took a couple days off for spring break, but I've been working on getting my models to share rendering resources. I'm afraid I have to let you in on a secret: not all MMO development is super exciting. Up until today, each time I loaded a model, I loaded its complete scenegraph including transforms, and allocated a material list, a vertex array and and a set of meshes for every instance of that model. If I had 20 identical objects on the screen, rendering all those duplicated meshes was really super inefficient.

I had a few requirements:

  • I wanted model loading to be asynchronous.
  • Models should share meshes, vertex arrays and material lists allocated by the renderer.
  • I wanted to be able to customize models individually (hiding submeshes, and moving/scaling/rotating model components.)

The asynchronous loading part was the simplest. I used an AVL tree template to build a model cache, then made a quick little task to do the loading when models aren't already in the cache, then schedule it in my task system. The asset system was already re-entrant so loading the model scenegraphs was no problem. Then I added a lock-free queue to put the finished models into and a step in the update to poll that queue for finished models, and Bob's your uncle. No possibility of long frames during model loading.

Once I had the loading working, the really tricky part started. In order to share renderables between model instances I used a super handy shared pointer template.

A shared pointer is a class which acts like a pointer and which has some additional logic which allows it to keep track of how many shared pointers are pointing to some memory and which only frees that memory if all pointers to it have been freed. These are super handy for shared resources since it makes all that reference counting stuff and garbage collection automatic. I already had a shared pointer template to use, so the big challenge was in actually using them.

The main downside to shared pointers is that you often find you end up having resources hanging around when you don't want them to. I spent a lot of time debugging my scenegraph (which also uses shared pointers to point to child nodes within the graph.) If you aren't careful, even the most benign of sources can cause problems.

The problem is with operator =. When you use operator = on a shared pointer, should the pointer you are copying give up its hold on the shared data or should both pointers point to the same data? In my case, both pointers end up pointing to the shared data. There are other variations on the smart pointer concept for different uses - go google smart pointer.

Each scenegraph node contains a vector of shared pointers to its children. When I remove an element from the vector, the last entry in the vector gets duplicated. This is fine for an int, or even most classes, but with shared pointers it is a big problem. Even though I thought I had removed all the entries from my vector, when I deleted the vector itself I found it contained 20 shared pointers all pointing to the same renderable node - oops.

By making all of my rendering resources managed by smart pointers, rather than the previous system where all resources were managed by the renderer, I discovered a nasty leak in my terrain stitching system that leaked a vertex array and mesh for every terrain block loaded. I love my Memory Tracker. It makes short work of bugs like this.

Making models customizable added another tricky problem. With each instance of the model pointing to the shared scenegraph in the model cache I was sort of stuck as to how to modify the transformations within that scenegraph. I spent quite a bit of time looking at OpenScenegraph, considering how they update the scenegraph during rendering and update. They use an assortment of specialized callbacks and visitors, but for what I wanted to do, their solution was overcomplicated. Modifying the scenegraph during the render seemed like an approach doomed to fail.

Instead I created a function to clone a scenegraph. I give it the model scenegraph stored in the cache and it creates a duplicate with all the same nodes, and shared pointers to the renderables. This way I have shared renderables, but they can be transformed and modified on a per-instance basis. The one hitch was that I needed rendering materials to be customizable per-instance for some models, so I had to add a facility to allow me to tell the model importer to mark material lists to either be duplicated or shared. If they're marked for duplication, then a whole new material list is allocated for each model instance. For models where that isn't needed, they also share the material list between all model instances.

In the end I spent a whole day debugging all the shared pointer issues and memory leaks to get the client to shut down cleanly. But now it does and I'm one big step closer to having things able to shoot at each other.

A few years back I worked on the Medal of Honour PSP User Hosted Server. The server had originally been built without the requirement to be able to shut down. Oddly enough, that actually isn't that unusual in the world of console development. But we were making a server for regular people to run and they might want to turn it off. When we first tried to shut it down we ran into system after system that crashed or just didn't work when being shut down. We hacked our way past the first few to get an idea of the scope of the issue. It was bad. To clean up all the systems and make them support being shut down it estimated out at a few weeks of work. We couldn't afford that. Luckily I had the bright idea of just calling ExitProcess when we were done. The app ended clean, problem solved. Total programmer time for the fix? About 10 minutes.

New Comment

Cookie Warning

We were unable to retrieve our cookie from your web browser. If pressing F5 once to reload this page does not get rid of this message, please read this to learn more.

You will not be able to post until you resolve this problem.

Comment (You can use HTML, but please double-check web link URLs and HTML tags!)
Your Name
Homepage (optional, don't include http://)
Email (optional, but automatically spam protected so please do)
What color is a lemon? (What's this?)

  Admin Log In



[Home] [Blog] [Video] [Shop] [Press Kit] [About]
Terms Of Use & Privacy Policy