• Advertisement
Sign in to follow this  
  • entries
  • comments
  • views

WIP: Week 3 -- Threading, Chunking, and Optimizations!

Sign in to follow this  


Hello all!

This week, I begun work on the threading model for the engine. Since having a thread for each component (rendering, physics, main game loop, input, etc) wasn't feasible and left a lot to be desired for the engine, I changed it to a Fork/Join or Job model. This allows easy use of a thread pool, which is easy and intuitive to use. I created a ThreadPool class that manages the queueing of tasks and the management of threads.

This was new territory to me, since I wanted to play with C++0x features like condition variables, return_type, std::futures/std::forward, etc. I also learned about more than the typical lambda function, and made the ThreadPool class accept any type of method: class method, static methods, functions, lambda expressions, etc. The interface looks like the following, and returns an std::future for the result:[code=:0]ThreadPool::create(std::thread::hardware_concurrency() - 1);// ...//auto = std::futureauto result1 = ThreadPool::enqueue([]{return "test"});//getInt returns 42.//auto = std::futureauto result2 = ThreadPool::enqueue(&getInt);// ..std::cout << "Result 1: " << result1.get() << std::endl;std::cout << "Result 2: " << result2.get() << std::endl;// ...ThreadPool::destroy();
I have the threads sleep on the condition_variable in ThreadPool until there's tasks for them to complete. Each call returns an std::future, from which the result is received.

Once I had the threading model, I started using it! I chunked out the world, and threaded different aspects of it (loading, unloading, generation, etc.). Below is an example of it working:

Screen Shot 2015-01-04 at 10.46.41 AM.png

Lastly, I noticed some issues with my mesh generation. It was taking 125-160ms, and that is WAY too long -- my Java version managed to generate chunk meshes in about 30ms. After a long time looking through the code, unrolling loops, etc., I was able to get it down to a whopping 22-25ms!! Now I can generate chunks (threaded) about 2-4 per frame, rather than one at a time! AND since I've got a Job framework going, I can have mesh generation take most of the frame time, and have the renderer grab the result and render it at the end!

This next week, I hope to start putting together a noise library for creating terrain. Alongside that, I need to start working on transparency (UGH It's already giving me a headache...) in order to handle water, liquids, and translucent materials.

Sign in to follow this  


Recommended Comments

This is looking good. I use threading exclusively for the chunk generation since generating voxels can be done independently, and the same with mesh generation.


I haven't thought about unrolling loops, though. My data is stored in 3D arrays and thus uses loops nested 3 deep for more readable code.

Share this comment

Link to comment

The real issue was when meshing (this time around), I was originally looping through the data 6 times, and I cut that down to 3. I also have found threading useful for some other tools, like sorting transparency and chunk order for rendering. 


As for 3D vs. 1D unrolled Arrays, it depends mostly on the compiler I believe. I've found a speedup going unrolled, but on other people's games they've seen a slowdown from the same move.....not too difficult to try out though! ;)


For readability, I still loop in 3D, but I access voxels like this:

_ushort getVoxel(int x, int y, int z) {
    // if you want to: assert (x, y, z) in chunk

    return _voxels[index(x, y, z)];

Keeps everything readable outside the method :)

Share this comment

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement