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

Major code speedup. Big factories ahoy!

I recently got sent a HUGE factory in a savegame for Production Line. It has over 600 production slots and 24 resource importers, and its MASSIVE. Here are some screenshots:

Despite its awesomeness it was MASSIVELY slow. Not only did loading it take forever, but whenever you changed any of the resource conveyors the game would hang for about 20 seconds. hardly ideal, especially given that I have an 8core i7 PC. So I set to work trying to optimize the code. I did some fairly small optimisations first, which boosted processing by 12% but I needed more fundamental re-design.  After chatting to a fellow indie coder, I agreed that my currents system of always calculating the optimum route to everywhere, for everyone, when something changed, was clearly not viable. I switched over to a new system of ‘lazy’ computation. Basically when I change a route somewhere, it now sets a flag on every production slot saying ‘you should recalculate your nearest slot soon’. That flag gets checked every frame, and sometime in the next 120 frames 92 seconds) each slot will calculate the route from its location to all the importers, and store the nearest two. It needs this so slots seem to import from a sensible location, as opposed to an importer that is miles away.

This was good, and sped things up a LOT. Now instead of hanging, the game would stutter for about 10 seconds. better but nowhere near good enough. I then realised I was doing something seriously dumb. I was going through all those routes I calculated, and picking the nearest, THEN doing it again, to pick the second nearest. doh! This sped things up, but in retrospect, it was trivial, it just alerted me (alongside my profiler) to the fact that when I tried to get (for example0 15,000 routes, I actually calculated 30,000. How come?

It turns out that the code that multithreaded all of the ‘calc the routes’ code, was not storing any of the results, so the code that came after it which then went through the (not saved) routes to pick the nearest ones was having to recalculate them anyway. I was essentially doing everything twice.l Worse…this second bit of code was single threaded, meaning all my multithreaded time was wasted and all the work happened in a single thread. What a dork.

A nice simple change to the code to make sure those multithreaded route calcs are actually stored *doh* means that not only did the processing time half, it now gets split over potentially 8 cores instead of 1, so on my PC its now running 16x faster. Combine that with the earlier speed-ups and its likely 18x faster, and because of the frame-spanning over 120 frames, it *feels* fluid as hell, even on insane maps.

Here is the concurrency visualiser showing the loading of the insanely big map.

And here it is zoomed in showing the multithreaded bits.

Those gaps are because each slot makes a single-threaded call to evaluate all of the import bays (each colored block is the code to get a route from one import bay to one production slot), and that call (in the main thread), then spreads it over the worker threads. Ideally I’d find time to eliminate that single thread blocking. Also I have some gaps in there because the end of each threads Calc() has to use a critical section lock to deposit its new routes safely in the main thread. Ideally I’d bunch them up inside a thread structure and dump them at the end.

Anyway, enough tech bollocks, the upshot is that massive factories will now be uber-fast :D.

New blog video, Production Line 1.07 and onwards…

Wow its been a BUSY week, like crazy busy. I released build 1.07 for Production Line, and started on 1.08. The price went up to $11 today, and I also started laying out a list of coming soon stuff in the forums here. There has been a steady (and accelerating) growth in daily pre-orders for the game. 4,000 sales seems ages ago now, and I’m struggling to keep up with all the feedback. Anyway…here is the new blog video.

I shouldn’t really be surprised, but the pace with which people have got around to building REALLY BIG factories is kinda amazing. This meant I needed to do some optimisation for those cases, often totally revising my idea of how big I should make certain fixed size buffers. (640k is enough for everybody!). I know some programmers might say ‘but dude…variable sized data structures. Yup, know all about them, but they can be SLOW. Try comparing the performance of a list or vector and an array one day… its HUGE. What I tend to do is have fixed arrays of oft-used objects, and allocate extra arrays if I start to run out, meaning rare one-off frame skips, but silky smooth performance most of the time.

My day is just now a blur of checking email;, then twitter, then facebook messages, then reddit posts, then forum posts, then forum messages, and then going through my bug list and my features todo list until its time to sleep or blow off steam in Battlefield One. However…just in case you thought that made me sound lazy, don’t forget we are also publishing another game (Shadowhand) which is coming soon. Here is Jakes latest video about the game:

Before long it will be GDC, and before that I’ve agreed to speak at TWO(!!!) events, which means…holy crap. I should stop typing and get on with it.. I will leave you with the exciting changelist for Production Line build 1.07:

[version alpha  1.07]
1) Major performance optimization for large factories.
2) Much faster vehicle rendering when zoomed out.
3) Fixed bug where very high car output resulted in no market price for a car being low enough.
4) Fixed the 'No room to TrashLowPriorityResource from stockpile' bug.
5) Fixed the 'Route Pending!' bug.
6) Possible fix for corrupt efficiency graph.
7) Fixed bug where manufacturing slots showed as not connected to a stockpile until the first resources arrived.
8) Added game-clock to top right menu.
9) Quitting the new car design dialog now gives a unique default name and price to the design.
10) Fixed bug where the warning notices on cars would persist until they encountered an empty conveyor belt.
11) Fixed bug where right clicking on research screen could delete items in the factory.
12) When a car is unable to move to the next (connected) slot, the message now tells the player which requirement is not met.
13) Background sound FX in R&D screen reduced in volume.
14) Fixed bug where some conveyor belt placements resulted in multiple placement sounds on top of each other.
15) Fixes to the layout of some R&D and slot menu items.
16) The warning and error notices in the factory now shrink to icons when zoomed right out.
17) As a temporary quick balance measure, only 4 valves are needed for engines now!
18) Fixed bug where supply stockpiles would not always fill correctly.

 

Anatomy of a bug fix

I thought it might be interesting to just brain dump my thoughts as I do them for a Production Line bug.

The bug is an error message which I get triggering thus:

GERROR(“No room to TrashLowPriorityResource from stockpile”);

Which is handy, as I know exactly WHAT is happening, and where in the code. Thats 95% of the work done already. Amen to asserts! I know that inside the function SIM_ResourceStockpile::TrashLowPriorityResource(), I am not able to actually do what the function needs to do. So firstly what does this function do anyway? I can’t remember… Thankfully there is a comment:

//if we find a resource not needed by current task, destroy it (only one)

Which is pretty handy. So this is a stockpile at a production task (working on a vehicle) and it needs room for a new resource presumably. The code goes through all of the objects currently in the stockpile to check we really need them. here is the code:

    iter it;
    for (it = Objects.begin(); it != Objects.end(); ++it)
    {
        SIM_ResourceObject* pobject = *it;

        //is this object needed?
        bool bneeded = false;
        SIM_ProductionTask::iter rit;
        for (int c = 0; c < SIM_ProductionSlot::MAX_RESOURCE_QUANTITIES; c++)
        {
            SIM_ResourceQuantity req = PSlot->CurrentResources[c];
            if (req.Quantity <= 0)
            {
                continue;
            }
            if (req.PResource == pobject->GetResource())
            {
                if (GetQuantity(req.PResource) > req.Quantity)
                {
                    bneeded = false;
                }
                else
                {
                    bneeded = true;
                }
            }
        }
        if (!bneeded)
        {
            RemoveObject(pobject->GetResource());
            return;
        }
    }

Thats the first half of the function, it then tries to do a simialr thing with requests, rather thanm objects. However I think I see an issue already. That GetQuantity() is checking the total quantity, not for this single object. Nope…thjat makes sense. I guess I really need to trigger the error and step through it, so time to fire up a save game from a player and run it in *slooooow* debug mode. Ok… an immediate crash from a save game..where we have 16 requests and no objects. So there seems to be a bug in the latter code:

    //ok we need to destroy a request instead of a current object.
    reqit qit;

    for (qit = Requests.begin(); qit != Requests.end(); qit++)
    {
        SIM_ResourceRequest* preq = *qit;
        //is this object needed?
        bool bneeded = false;
        for (int c = 0; c < SIM_ProductionSlot::MAX_RESOURCE_QUANTITIES; c++)
        {
            SIM_ResourceQuantity req = PSlot->CurrentResources[c];
            if (req.Quantity <= 0)
            {
                continue;
            }
            if (req.PResource == preq->PObject->GetResource())
            {
                //sure we ordered it, but do we actually have enough for this task, when com,bining requests with objects?
                int stock_and_ordered = GetStockAndOrdered(req.PResource);
                if (stock_and_ordered >= req.Quantity)
                {
                    Requests.remove(preq);
                    preq->PObject->Trash();
                    return;
                }
                bneeded = true;
            }
        }
        if (!bneeded)
        {
            Requests.remove(preq);
            preq->PObject->Trash();
            return;
        }
    }

So what could be wrong here? Ok…well hang on a darned minute. This code is just saying ‘do we really need resources of this type right now???’ rather than ‘do we need ALL of these right now?’  which is more of a problem. We are clearly somehow ‘over-ordering’. The slot in question is ‘fit trunk’. presumably it only needs trunks… They do all indeed appear to be the same type. 4 are ‘intransit’ the rest are queued up at some resource importer or a supply stockpile or component stockpile somewhere.

So the ‘solution’ is easy. First kill off (delete) a request that is not in transit yet, to make room, or if that is not possible, kill one that actually is in transit. This then frees up the required place in our stockpile. But hold on? this is just patching over the wound? how on earth did this happen? The calling code is requesting new resources, when clearly 16 are already on the way! in fact its requesting a servo…

AHAHAHAHA!

what a beautiful bug. The player obviously upgraded to a powered tailgate, which means a servo is needed for every trunk, and yet 16 trunks were already on order. Error! error! I have special code that handles when an upgrade makes an existing resource requirement obsolete (climate control replaces aircon), but not code to handle this case, where additional stuff is needed. This is FINE, I can implement the above mentioned code without worrying how it happened.

That actually wasn’t too bad at all :D

Production Line 1.05, with video and arghhhhhh

This week has been tough. I’ve been busy, also been ill, and been totally rewriting a core bit of code in Production Line to fix some obscure bugs. I think the code is way better now, but the proof is in the play-testing. I didn’t want to keep the improvements I *know* I’ve made hidden from people any longer so today I released 1.05 and also this video…

Here is the complete changelist:

[version alpha  1.05]
1) Resource conveyors now have feint shadows.
2) New design and car stock dialogs now prevent autosave from triggering.
3) Powerplant efficiency upgrade now works in a more obvious way.
4) Massive speed-up to the delay between a stockpile needing resources, and them being ordered.
5) Floating values and particles no longer visible when quitting a game.
6) Fixed crash bug when preparing to place a new slot over a conveyor belt loop.
7) Game now paused whilst in R&D screen.
8) Erroneous robot upgrades removed from make windows slot.
9) Changes to floating values rendering makes zoomed out GUI less messy.
10) Fixes for possible causes of crashing when saving really big games.
11) Some crude rebalancing of starting funds for the first two missions.
12) ‘R’ as well as ‘r’ now rotates.
13) Clicking current balance now acts as an on/off toggle on finance window, which is no longer modal.
14) Various minor GUI fixes.
15) ‘Make valve’ is now visible on the slot picker.
16) Save game GUI now much more consistent with the load game GUI.

This code has been hell to get right. Its all to do with the resource items that get delivered around the factory. They can be created new (nothing is actually new() for memory reasons) and imported from an importer (or held in an importers queue), then can be in transit, then can be used up, they can be created by manufacturing slots, or heading to/from a supply stockpile or in an export queue for either of those things. Thats all EASY to manage. The code hell comes from the fact that the player can delete ANYTHING at any time, or change any route which makes some of those items ‘stranded’. You can delete the source of a resource, or some part of its current route, or its destination at any point, and code chaos ensues.

I think its much better now… I really hoper so, its actually stressed me quite a bit.

Ultimately all bugs can be fixed, but its frustrating because a) I have a lot of people playing and want them to be happy and b) I have a long list of ‘cool things’ I want to improve and tweak and add, and I’m not letting myself do any of that stuff until I have little or no bug reports, so its been really frustrating. I’ll be sooo happy if people say I’ve fixed some of the bugs and I can go back to doing some GUI improvements and some play balancing and tech tree fun and games!

The alpha continues to be really, really popular, almost entirely by word of mouth (not much press has come our way), so that’s really encouraging and I’d like to thank everyone who has tweeted, posted in forums, and generally told their friends about the game, it is much appreciated. I’m really hoping next week my video will be all ‘look at all the coool stuff we added!’.

Todays big patch(1.04) and starting to promote it (a bit)

This has been the softest launch of a game I’ve ever done. I spent about $100 total on facebook post boosts, I tweeted, I blogged and I posted to the ProductionLine facebook page. Since then…thats it, I’ve been pretty much going along on word of mouth, and even then, sales have exceeded my expectations! This is really good news, because so far the development of the game has gone exactly as I had hoped, with a lot more focus on what actual players of the game want, rather than me guessing, or doing just want I want, or me trying to guess what makes the press happiest.

This has resulted in a lot of bug reports! (many thanks for that) and some really good suggestions and ideas, some of which have already made it into the game. People do seem to be surprised how quickly stuff goes in or gets improved, but frankly thats because I worked on this game for about a year in silence so there is this whole huge library of decent engine code in the background that is *done* and thus I only really have to code new features and GUI stuff now. New GUI does not take that long, and thankfully I’ve got good enough at debugging multi-threading and recursive stuff that this is not a huge bottleneck either. I’m almost disappointed nobody is having frame-rate issues, because I love optimizing :D

This is just as well as there have been a LOT of ideas and suggestions. I’ve already seen factories way bigger and more efficient than anything I have managed to create. It never occurred to me to re-use the conveyor belts in cunning roundabout-style loops with the individual processing elements happening at different junctions…until someone found a bug in it.

Users feedback has been excellent, encouraging and invaluable.

But anyway! I’m actual;y sending out a puny mailing list today with 7,500 recipients, so that should open things up a bit, especially as some are press. I don’t expect massive press coverage, but I’m not relying on it either. The game remains very much in Alpha (not even beta) so I expect a lot of people, gamers and press alike will stay in a ‘wait and see’ mode.

In the meantime, I have just set a big patch(1.04) live, and here is the fairly hefty changelist. (not bad for about 4 days work).

[version alpha  1.04]
1) The task ‘make fuel tanks’ now unlocks when researched correctly.
2) Fixed some crashes and routing bugs caused by deleting resource importer bays.
3) Pop-up details on the slot-picker now should show decimal places for times.
4) Vehicle details windows limited to one per vehicle and can now be dragged by the player.
5) Fixed minute format bug in save games.
6) Pause now works as a toggle, and all speed controls have hotkeys.
7) Escape key now closes slot picker.
8) Slot picker has less visual ‘padding’.
9) Double-click on the relevant window now loads a save game.
10) The upgrades section of a slot details dialog is now hidden if there are no upgrades available for selection.
11) Any open dialogs are now correctly closed when going to the main menu.
12) Fixed crash bug when a single stretch of uninterrupted conveyor belt was over 64 tiles long.
13) Added new efficiency dialog which shows efficiency over time and also a snapshot of current slot efficiency.
14) Fixed bug where slots could be placed ‘spilling’ over into a locked factory area.
15) Fixed bug on low resolutions where the slot upgrades window did not fit on the screen.
16) Floating numbers fade out now even when paused.
17) Improvements to ‘load-balancing’ at junctions.
18) New ‘Efficiency’ dialog currently just showing global state of all slots now and over time.
19) Slight speedup of creating the load-game dialog.
20) New vehicle pop-ups show the reason a vehicle is stuck.
21) Some conveyor belt graphics now have darker, more obvious direction arrows.
22) Fixed incorrect sizes of some delivered resources.
23) New upgrade for painting slot: ‘High pressure paint nozzles’
24) Fixed bug where components built inside the factory at ‘make’ slots did not survive a save and load.
25) Corrupt resource deliveries to roof making and similar slots fixed.
26) New graphics for the tyre-making slot and the window making slot.

 

Thanks for everyone pre-ordering, and I also really appreciate it when people tweet or post online about the game, its really helpful. If you don’t have the game yet, here is the order form :D