Game Design, Programming and running a one-man games business…

Coding a load-balanced multithreaded particle system

Background: I am coding a 2D space-based autobattler with ridiculous levels of effects called ‘Ridiculous Space Battles‘. I code my own engine, for fun, in directx9 using C++ and Visual Studio.

Because I love coding, yesterday I found myself wondering how well the multithreading in my game was holding up. So I fired up the trusty Concurrency Visualizer in Visual Studio. I love this tool and have used it a lot on previous games. One of the biggest demands on the CPU for my game is particle processing. There are a LOT of explosions, and other effects, and a crazy number of particle emitters (thousands of emitters, hundreds of thousands of particles). Obviously this would normally be a code bottleneck, so I have a task-based generic multithreading engine that handles it. The main thread builds up a bunch of tasks, the threads grab the next task on the list, and the main thread will wait until the task list is empty. If there are still tasks in the queue, the main thread will do one as well. So how did things look?

Disastrous!

So what is going wrong here? I have a generic ‘marker’ set up to show the span of the main thread’s drawing of the game (GUI_Game::Draw()). Inside that, a bunch of unlabeled stuff happens, but I added spans to show when the multithreaded task called UPDATE_PARTICLE_LIST is called. There are two massive things going wrong here. Firstly, there are only two other threads joining in to process the particles, and secondly one of those updates seems to take 20x as long as the other. Worse still, its the one the main thread chose… so its a huge bottleneck. Technically this is still a speedup, but its marginal. How have I fucked up?

Some background to my rendering algorithm is needed: The game has 2 blend modes for particles. A ‘Burn’ mode, that saturates color and is used for fire, lasers, sparks etc, and a ‘Normal’ mode for smoke and debris etc. The particle effects are batched as much as possible, but I cannot mix those blend modes in a draw call. Also, some particles are below the action (the ships) and some above, to give a semi-3D look and make it look like the explosions engulf the ships. So this means particle emitters fall into one of 4 lists: NormalBelow, BurnBelow, NormalAbove, BurnAbove. This is all fine and works ok. In action, the game looks like this:

Because you can freeze frame and scroll around, everything has to be properly simulated, including particle effects currently offscreen. Anyway it all works, and I have FOUR particle emitter lists. So naturally, to load-balance everything, I gave one list to each thread and considered the job done.

BUT NO.

It turns out that those 4 groups are not equal in size. They are laughably unequal. The ‘BurnAbove’ list contains all of the fire and spark emitters on all of the pieces of all of the hulks from destroyed ships, plus sparks from fiery plasma torpedoes, expended drone explosions, and missed or intercepted missile explosions. Thats MOST of the particles. When I checked, about 95% of particles are ‘BurnAbove’. I had 4 lists multithreaded, but they were not vaguely really load balanced.

Once I realized that the solution was theoretically easy, but fiddly to implement and debug. I decided I would add a new load-balanced list system on top. I created 8 different lists, and when an emitter was created it was added to the ‘next’ list (the next value circled round through all 8), and told what list it was in. When it was deleted, it was removed from the appropriate list. Note that ‘deleted’ is a vague term. I delete no emitters, they get put into a reusable pool of dead emitters, which complicates matters a lot…

So in theory I now have a nice load-balanced series of 8 lists that contains every particle emitter that is currently live. The original 4 lists are still valid and used for rendering and blend mode data, but this ‘parallel’ list system existed alongside it, purely to handle load-balancing. What this means is, that a load-balanced-list may contains particles from all 4 render groups, but this does not matter as I am running update code on them, not rendering!

It didn’t work.

Crashes and bugs and corrupt data ahoy. I worked on it for ages, then watched a movie to try and forget it. Then this morning, after some digging, it was all fixed. What actually was going wrong was related to smoke plumes. Because there are a lot of smoke plumes, and they always reuse the same particle config data, they exist in a separate system, updated separately. I had forgotten this! And what was happening was my new load-balanced lists stupidly included these emitters when they should have been kept out of it. The emitters would expire and be deleted in the multithreaded code, then later accessed by the plume code. CRASH.

I worked it out this morning before breakfast! I was very pleased. You might be thinking, what about the only 2 threads thing? LOL I had hard coded the game to use maximum of 4 threads, probably as a debug test. Idiot. I just changed it to be 10 and everything worked:

This is more like it. I wasted ages trying to get the dumb concurrency visualiser to show my custom thread names instead of ‘Worker Thread’ but apparently thats the category. Not much help. FFS show us the thread names! (They work in the debugger). But anyway, that image above is a snapshot inside a busy battle for the GUI_Game::Draw() showing how UpdateParticles tasks get spread over 8 threads. I’m still not sure why that sixth thread misses out on a task, which gets nabbed by the main thread…

Anyway, the point is it works now, and in theory updating particles is 8x faster than it would be with single threading. I do need to apply the multithreading to a lot more of the game code to get the best possible results. I am testing this on a fairly beefy GPU and CPU (Ryzen 9 5900X 12 Core @3.7GHZ and RTX 3080) in only 1920×1080 res. I want the game to look awesome at 5120 res or on a five year old cheap laptop, so plenty more to do.

If for some reason this tips you over the edge to wishlist the game, here is the link :D.

Solar farm: six months of proper generation plus REGOs.

If you follow this blog a lot, you will know that we actually started generating power on our solar farm in October 2024, but there was some downtime in November, and then more downtime in December due to an extreme storm damaging the site (we lost 10 panels, since replaced), so we didnt have a straight six months of data until yesterday.

Also, because of the way solar is generated over the year in the UK, you want either jan-june or july-dec in order to extrapolate. The output of solar panels is basically a bell curve, peaking mid year, so once you have six months of data, you can make an educated guess (but only a guess) about the whole year. Just who shallow or steep that curve is will depend on a few factors, but in general I think that improvements in panels has flattened that curve a bit. Lots of technical changes to panels mean that they are more tolerant of ‘partial shade’ than they used to be. Handy in a cloudy country!

So how much power have we generated in six months? well its…. 766,739kwh. (766 MWH). So we can assume that if this is a typical year, that would mean 1,533MWH in a year, or 1.5 gigawatt-hours. Enough to run the average TV for 1,700 years. Or enough to fill a large electric car from empty 19,000 times. In some ways this seems a lot, but how does it compare to what we expected?

Well it depends… If I dig up the oldest email about the sites initial plan, the assumed generation output was just 957MWH so this sounds amazing. That was then changed to 1,245MWH it was later changed to 1,347MWH . The final setup for the export meter seems to say that the expected output is 1,339MWH. So the actual expectations are all over the place, but all below the current value. I THINK that we had a very very good April, and that is skewing the results. If we hit 1,400MWH in the year that will be very pleasing. It is too early to really be definitive about whether or not this means that the farm makes a profit. I would be nervous of judging that without a full year. Especially as a maintenance contract is still not signed yet.

In other news…

You may have read posts by me banging on about REGOs. A REGO is a Renewable Energy Guarantee of Origin certificate. When you generate a single MWH of power from a renewable source, you can tell a government regulator (ofgem), and they give you a certificate. There is no subsidy, but you can sell them. Who buys them? Companies and energy retailers who want renewable energy. To be able to sell your power to customers as 100% renewable, you need to buy REGOs to cover all your energy. There is an open and competitive market for REGOs, and currently they are worth £10-£15 per MWH. Thats equivalent to 0.1 to 0.15 pence per unit of electricity, no not a lot from a consumer POV. Like most companies, we have our REGO sales ‘bundled’ in with the PPA (Power purchase agreement) we have with the company who buys our power. In this case OVO Energy.

Thats the theory. In practice, this process is HELLISH. It genuinely feels like ofgem have been told to make the process of getting accredited as impossible as they can. The amount of bureaucracy, inefficiency, radio-silence and pickiness over the grammar and punctuation in every piece of text in every document required in order to qualify is beyond insane. I applied as SOON as was possible (your site has to already be generating, which is stupid as hell), and it still took SIX MONTHS to basically have a form processed and accepted. We even had to argue basic maths with them, such as arguing that 400+500 = 900, and not as they claimed… 810.

Anyway, that insane process (which reminded me of the DNO legal process, which reminded me of the planning process….etc) is finally complete, although not without a formal complain to ofgem about it. And I will not bore you with the details, but suffice it to say that obviously they are still arguing about it, and saying its not right, even though they have approved my site, and approved my output data, and credited me with the certificates which I have lready sold. Its insane beyond words.

And actually the entire process is a colossal waste of time, because there are already a network of organizations who entire existence is simply reading meters and verifying that they are accurate, and reporting that data online. I am charged about £250 a year for this ‘service’ (the real cost is likely under £1, its just a meter). Now ofgem could trivially just connect to the databases for those independent companies, read everyone’s data automatically, and credit the REGOs without any human interaction whatsoever. And as for verifying that the data really is renewable, the people who 100% absolutely independently KNOW what equipment is connected, are the DNO (Energy distribution network operator). There are a handful of meter readers, and a handful of DNOs, and this would be easy…but NO! Lets set up a torturous six month minimum process that involves websites that crash, tons of paperwork, complicated rules and processes and a staggering waste of everybody’s time. Because thats the UK energy industry. Are you surprised your bills are high?

So yeah… its been interesting.

I am HOPING that within a month, I will have stopped getting irritating emails from ofgem, I will not get any more letters about lawyers and leases, from one set of lawyers to another (I would love to jettison the entire profession into the sun at this point), and have a maintenance agreement signed.. and then finally I will be able to basically forget about the farm, apart from enjoying checking the stats!

I do still LOVE the fact that I built it, and own it though. Its awesome :D.