Multi-threading terrain generation requests

Started by
3 comments, last by NineYearCycle 16 years, 6 months ago
Beware this is a lot of thinking aloud stuff and a request for your eyes to glance over it :) I have a set of processor intensive terrain generation steps, at the moment these run in a main thread which causes it to halt whilst it deals with them. This causes some damned ugly frame rate chugs. At the worst these can last for a few 10s of second (30+ on this 2GHz pc). The idea is to break these terrain generation calls out to be done in another thread as I've got a dual-core cpu but by doing everything in a single thread I'm only using 50% of my processing resources! Once each of these jobs is done it needs to hand back the generated data to the caller. Or would it be better if the caller polled the status of the job? Yeah actually that sounds like it makes more sense as the caller has to maintain information about the job so that it doesn't try and generate the new data request again. Ok so with that in mind, I have an idea of the flow (and not much more):
  • Thread 1: Each terrain node is tested to see if it needs to split (its a quadtree LOD scheme) and when it does it generates a request for the terrain generator which is running in another thread.
  • Thread 1: At each subsequent split test it checks the status of the request to see if it's complete.
  • Thread 2: Meanwhile in thread 2 the terrain generator is generating the data. Once the generation is done it moves it into a completed list and moves onto the next request, repeat ad-infinitum for each request.
  • Thread 1: Back in requestee thread 1 the split test is done and the data is determined to be ready, so it pulls the data back out of the request and generates the next level in the quadtree.
Now several complications arise which may negatively affect performance as there will have to be some mutex'ing, locking and or critical section stuff. The renderer will be running in "Thread 1" along with update logic and some other tasks. This handles all the in game textures which "Thread 2" will need to request from the pool, generate some data into and then assign handles to the terrain node its building... actually that should be fine, requests for textures from the free texture pool will most likely only be coming from the terrain generating thread anyway since this is just a tech demo idea. Even if they aren't they're just a request for a free texture so aren't gonna hold things up. Hmm, I can't actually think of many other problems so what do people think of the basic concept? Is it horribly flawed or perfectly logical? Oh and are there any real gotchas that I should beware of using something like the system that I've just described but not yet implemented. Thanks for your time folks. Andy (aka NineYearCycle) EDIT: after chatting offline to *someone* they pointed out that I should mention that the terrain generator knows almost everything it needs too about the terrains parameters so it doesn't have to communicate with the outside world during the generation except for requesting the resources. The only data it needs from the requestee are the bounds of the area that it must generate.

"Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile"

"Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgement difficult."

Advertisement
Right, given that the worker thread apparently requires little input, this should be simple really.

Thread 1 collects all data that Thread 2 will need to do its job.
Thread 1 starts off Thread 2 with a copy of this data.
Thread 2 does all its processing and comes up with result data.
Thread 2 then needs to lock the relevant structure and throw that data in.
Thread 1 finds new data in that structure and handles it accordingly.

The only things you really need to consider are the shared resources. One is the structure I mention at the end, for passing the data back. Usually something like any std::container is fine here; just protect all access to it from both threads with a mutex, eg boost::mutex. Thread 1 polls it periodically to see if it's empty, and if not, takes an item off, etc.

The other is the texture pool - why does thread 2 need to use this, and what for? Can it not be done either as part of the input to thread 2, or fixed up by thread 1 after thread 2 returns data? If not, just protect access to that pool with a mutex, too.
My version is rather simple...

I simply generate the full resolution in the background, and locking the terrain while doing so. When generation is complete, I generate the indices from the full resolution mesh at runtime.

The fullres generation could of course be a problem with a highspeed moving camera, but for my needs (car combat over a LARGE area) this works like a charm, and is really fast too once the mesh is generated!
"Game Maker For Life, probably never professional thou." =)
Quote:Original post by Rasmadrak
My version is rather simple...

I simply generate the full resolution in the background, and locking the terrain while doing so. When generation is complete, I generate the indices from the full resolution mesh at runtime.

The fullres generation could of course be a problem with a highspeed moving camera, but for my needs (car combat over a LARGE area) this works like a charm, and is really fast too once the mesh is generated!


Nice system but in this case I plan to do some work getting it to generated planets which precludes me pre-generating the full-resolution vertex mesh down to 1-metre for the whole object [grin]

The problem in this case isn't really that terrain generation, there's hundred of articles on that and I've written several terrain systems before. My main problem is in coming up with the scheme for implementing it as a split multi-threaded system, with the mesh/texture generation happening in another thread. However as Kylotan has said, it all seems quite simply really. I'll give it a go tomorrow and report back.

Andy

"Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile"

"Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgement difficult."

Well I went ahead and implemented something like the original idea I had in an older terrain system I have lying around. It took about 2 hours to get everything up and running mostly due to the nature of the old method but wahey it worked a charm!

Hopefully I'll get some time to do it properly using the factories I mention before but the early test has been really effective. Currently this naive implementation causes quite a bit of blocking during rendering and generation as the objects generate their own children rather than that being done in a separate class. However that won't be the case in the actual implementation.

Thanks for the help everyone it really was almost as simple as I'd hoped but not dared believe :D

Andy

"Ars longa, vita brevis, occasio praeceps, experimentum periculosum, iudicium difficile"

"Life is short, [the] craft long, opportunity fleeting, experiment treacherous, judgement difficult."

This topic is closed to new replies.

Advertisement