Skip to content

Conversation

@Halbann
Copy link
Contributor

@Halbann Halbann commented Nov 26, 2024

Problem

There's a long freeze whenever you open the craft browser that's proportional to the number of craft. You'd think it was because the game is reading the .loadmeta files for each craft, but it's also proportional to the overall number of parts, so it must be reading the craft files themselves to an extent.

dotTrace (release player, with KSPCF) shows that, from the start function of CraftBrowserDialog, significantly more time is spent in CraftEntry.GetThumbURL than is spent actually reading the .loadmeta.

This is entirely due to ShipConstruction.CheckCraftFileType, called once per craft, which performs a ConfigNode.Load on the craft file itself. It does this solely so it can work out the filename to find for the thumbnail (e.g. default_SPH_Miner.png) by looking at the type field in the craft file (SPH or VAB).

ShipConstruction.CheckCraftFileType is also called in the WrongVesselTypeForLaunchSite pre-flight check when you launch a vessel.

BuildPlayerCraftList can be called twice in one frame, and once more in the following frame, depending on the context, but this doesn't always rebuild the craft list, only the UI. The unnecessary work still slows the open time down by about 30%.

The time is takes to search by name also scales very badly, proportional to the number of craft. The list is filtered by CraftSearch.SearchRoutine, which yields for a frame after checking each entry, regardless of the elapsed frame time.

Changes

  • Speed up CheckCraftFileType by avoiding loading the whole craft file, instead only checking the file path. The type field in the loadmeta could be used here if it turns out that the path does not always contain the facility name, although if the loadmeta file doesn't exist the craft file would need to be read for it to be generated.
  • Prevent CraftBrowserDialog from rebuilding the craft list twice in a frame.
  • Prevent the craft list from rebuilding yet again on the next frame.
  • Disable the call to Debug.Log when the thumbnail is not found.
  • Replace CraftSearch.SearchRoutine to prevent the coroutine from yielding for a frame on each entry. Instead use an 8 ms budget, and make some small optimisations such as avoiding the use of GetComponent.
  • Reduce the wait time after the last keystroke to begin filtering from 0.25 to 0.1 seconds.

Profiling

81 craft files totalling 75 MB
Release player, KSPCF including ConfigNodePerf

BuildPlayerCraftList Before: 2,674 ms
BuildPlayerCraftList After: 271 ms

Flame Graph Before:

image

Flame Graph After:

image

Opening the craft browser is still not fast enough to feel responsive with this many craft. Maybe in future the entry creation could be put into a coroutine that populates the list over several frames. I did investigate multithreading the code using Parallel.ForEach to create each CraftProfileInfo (read the loadmeta files), but ConfigNodePerf isn't intended to be thread safe.

@gotmachine gotmachine added the kspPerformance Possible performance improvement in KSP label Mar 8, 2025
@gotmachine gotmachine merged commit 0b11d55 into KSPModdingLibs:dev Mar 9, 2025
1 check passed
@BrettRyland
Copy link

BrettRyland commented Mar 9, 2025

I'll be interested to see how this performs compared to the custom craft browser dialog I created in BDArmory's version of the vessel spawner mod where craft meta-data is cached between uses (it's cleared when the "BDA Vessel Mover" window is hidden), which allows for an effectively instant new dialog when used multiple times. KSP's built-in craft browser dialog seems to start from fresh each time the dialog is opened, which causes a significant delay when used multiple times in a scene.
The first time BDA's craft browser dialog is opened or the current folder is changed, it only reads from the .loadmeta files if they're up to date, otherwise it generates new ones, which can take a bit longer, but this is a one-time cost. Benchmarking on my machine with 267 craft files (55MB) using System.Diagnostics.Stopwatch in CustomCraftBrowserDialog.UpdateList gave around 250±50ms to generate the craft list when all .loadmeta files are up-to-date and around 1100±100ms when all .loadmeta files were missing (which requires reading the .craft files).

Additionally, filtering is applied immediately whenever the selection filter or craft list is updated as it's just filtering a cached list of strings, which doesn't appear to give any noticable delay.

@Halbann
Copy link
Contributor Author

Halbann commented Mar 10, 2025

Caching loadmeta files (or rather the CraftProfileInfo objects?) is a good idea, especially for when you're in the editor and checking out a bunch of different craft. It should take about 40% off the current time. Not sure when we should reset it, perhaps never?

I tentatively remember the majority of the time spent filtering is calling SetActive on a lot of UI game objects, which causes Unity to do a lot of UI work. Maybe there's a smarter way to turn on/off lots of UI at once? I'm guessing you're using IMGUI so you don't pay that.

I'm trying to avoid modding at the moment, but those are my thoughts.

@BrettRyland
Copy link

Yeah, BDA's custom craft browser is using IMGUI, so it's simply a matter of filtering the list of strings and then only drawing the appropriate entries. No need for messing with game objects.

In any case, I look forward to this actually being released (not just a pre-release), so I can update through CKAN.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kspPerformance Possible performance improvement in KSP

Development

Successfully merging this pull request may close these issues.

3 participants