Multithreaded drawing and logic

Started by
10 comments, last by Scourage 10 years ago

I've decided that there's too many problems with multithreading, so I'm going back to single-threading. Mutex locks everywhere, not knowing if I might be missing one somewhere...

Probably a good decision based on what you described.

Developing a multi-threaded game is an engineering exercise. It requires planning, and the execution requires discipline. It uses a lot of the "science" aspect of "computer science".

You must carefully partition tasks, coordinate communications between tasks, map tasks to processes, and ensure the protocols for communication are followed. There is an area of computer science fully dedicated to parallel processing and concurrent computing. People get doctorates in that sub-field.

A mutex is one potential part of the process of coordinating communications between tasks. If your code style just says "I think I need a mutex here" rather than specific rules about when and why you need or do not need them, then you probably have insufficient architecture for a multiprocessing engine.

Advertisement

Multithreading is hard, but it's not impossible. You do need to think of your game slightly differently in terms of data and processes. I like to do things with events, commands, and worker threads. I don't have any mutexes or locks in my system, but I do design my data structures so that they are contention free for the duration of the worker thread's processing. I use lockless queues to pass events/messages between threads (typically updates from my entity system to the main thread for dispatching later in the frame/next frame).


The result of one unit's update might depend on another, so this isn't a solution for lockstep multiplayer games.

I've run into that before, what I've done is maintain several buckets of entities to update based on a parent. I've never really got further than 4 or 5 deep. I then update all the entities in bucket 1 (since they don't depend on each other or a parent), then 2 (their parents are all up to date), etc, etc. This requires a little extra book keeping, but definitely possible.


Sending them off to the GPU in each worker thread? They share the same GL context and would have to constantly switch with wglMakeCurrent. Plus what if they require different shaders?

You don't have to actually call any GL commands from the worker threads, just get everything ready to draw (cull and uniform setup). If you're doing some cool multidraw-indirect stuff, you can have each thread touch the command buffer, but that requires some rather specialized data structures and beyond what I know about. In my rendering engine, I've encapsulated rendering of objects into a command class. Worker threads generate all the command classes in parallel (each renderable object generally caches and reuses it's render commands). The command classes get their uniform data updated (not calling glUniform, just holding the vector/matrix ready to call glUniform). When the worker threads are all done, I then combine each worker threads command list, sort by render state and then process the entire list calling exec on each command. This is where the GL calls are actually made. You could even push the entire sorted command list into a separate thread just for rendering.

Cheers,

Bob


[size="3"]Halfway down the trail to Hell...

This topic is closed to new replies.

Advertisement