UltraJohn / HUDReplacer

A mod that allows you to replace the HUD/UI of Kerbal Space Program.
Other
8 stars 1 forks source link

[Performance] This mod adds almost 1 second to scene switching #7

Open HebaruSan opened 3 months ago

HebaruSan commented 3 months ago

Hi! The Eye of Sauron is upon this mod. Apologies if some of my info here is incomplete; I'm attempting to summarize some ad hoc code review occurring on a Discord channel.

image

Opportunities to speed up:

UltraJohn commented 3 months ago

Hi!

I am indeed aware of the cost of these loops, however as I'm not a super advanced programmer by any means, this is what I came up with as my solution. I decided that I was okay with the current implementation until I could further optimize it, but I haven't really found any better solution yet. Though I don't really think it impacts the gameplay at all when the texture swap happens during a scene load.

I have unforfunately already attempted only doing this load as a one-time, but as it turns out, some scene switches in the game will unload all the textures and reload them again, causing the texture swaps to revert back to the original. This means that I still have to run it on every scene switch, in case you happen to switch from a scene where it resets.

Edit: I am ofcourse open to anyone who would like to help improve this mod any way they can! As a side note, I was thinking about changing the licensing on the mod in case that would be necessary. I just don't have much time lately to work on modding, unfortunately.

JonnyOThan commented 3 months ago

I’m not very familiar with Unity UI but I’d assume most of these textures are referenced from some prefab object which gets instantiated, right? So if you modify the prefab to point to a different texture, then no further modification needs to be done. Unless the prefab itself is being unloaded and reloaded…but then you just hook into the places where that happens (using harmony if necessary).

These are all just guesses so maybe I’m way off base about how this needs to work.

UltraJohn commented 3 months ago

I have looked through how to modify the prefabs directly, but I couldn't find anything useful. From what I've read prefabs are serialized gameobjects that are only able to be instantiated. All assets are read-only during runtime, so they are impossible to edit. I have also looked for how unity handles the loading of the textures, and see if there was a way to use harmony to redirect the file paths to the custom ones instead, but also no dice there.

Ofcourse, another solution would be to directly patch the game files to replace the textures entirely and that's easy to do (I have done it for other games), but that requires modifying game files and I would rather have something that is not a permanent modification (and may require running an exe.)

JonnyOThan commented 3 months ago

prefabs are serialized gameobjects that are only able to be instantiated. All assets are read-only during runtime, so they are impossible to edit.

I’d have to look into it a bit more but in my experience once the prefab is loaded it’s no different from any other gameobject (other than generally being inactive and marked for ignoring garbage collection etc). When you say “assets” here do you mean the assetbundles themselves, the prefabs, or something else?

If that doesn’t pan out then there are more invasive things you can do with harmony. For example Shabby patches every call to Shader.Find and redirects it to its own code so that it can replace shaders no matter where they are referenced from. The patching itself takes a second or two at load time and then everything else is very fast. It has to be done this way because Shader.Find is a native method so you can’t apply a prefix patch to it like you can do with a managed method.

BepinEx also has a patching system that can do the kinds of replacements in the game files you’re talking about but it’s generally not compatible with KSP. But something like that might work as a last resort.

UltraJohn commented 3 months ago

By assets I mean after they are loaded from disk into memory from the assetbundle files. I'll have to do some more research, but as I understood it, these are not able to be modified in any way during runtime. Maybe I'm wrong.

Yeah I was actually looking for that exact thing like Shabby does, though I was unable to find any method call that loads the asset files (I was looking through the dll's through ILSpy.) I eventually settled on the current implementation until I could figure something like that out, but alas I'm not that adept. I'd love to figure it out sometime though. Any guidance would be welcome. I love learning new things!

HebaruSan commented 3 months ago

Even if the replacements do have to happen every scene change, they're always the same replacements for a given play session, right? Could you do the costly O(M*N) loop once at start-up, store which textures needed to be replaced, and then just apply those changes later without repeating the exhaustive search? Or if different replacements happen for each scene, then the caching could be per scene?

UltraJohn commented 3 months ago

You have a valid point, though I fear it's a bit more complicated than that as not all of the Texture2D objects exist initially on first startup scene. If for example the textures for the navball only exists during flight, then you still need to run through the list to find the correct objects for that scene, before you can cache it. Perhaps then as you say it would be possible to run it once per scene and thus have it cached on a per-scene basis for the rest of the play session.

The problem with that is that if these objects are unloaded between scenes, then I cannot cache them, as they would become null and thus a new search would be required. Unless I'm missing something obvious?

HebaruSan commented 3 months ago

The problem with that is that if these objects are unloaded between scenes, then I cannot cache them, as they would become null and thus a new search would be required. Unless I'm missing something obvious?

A static object in the class would persist between instances.

UltraJohn commented 3 months ago

Right, but the game objects that store the UI elements are not static? So even if I stored them in a static list, they would null on scene change, no?

HebaruSan commented 3 months ago

Oh sorry, I misunderstood; I see what you meant now.

I would look for a way to look up a known object by some key. What about caching Object.GetInstanceID(), then calling Resources.InstanceIDToObjectList()? I don't know enough about Unity to guess whether the instance IDs would be consistent within a session (the documentation says "The ID changes between sessions of the player runtime and Editor" :shrug:), but maybe they would?

UltraJohn commented 3 months ago

I will take a look and see if that's a possibility, or perhaps some other way.

Thank you both very much for the help!

JonnyOThan commented 3 months ago

Runtime editing of prefabs that were loaded from assetbundles is definitely possible. I’m pretty sure that would be optimal for performance. Then it’s only a question of when and how. If they are unloaded and reloaded then you need to do it after each load. And if nothing else, Harmony should be able to inject something at just the right place in code to alter the prefabs before anything can be created from them. But again I haven’t looked at this issue so I might be missing something

JonnyOThan commented 3 months ago

Oops, I may have been operating on some bad assumptions there...

Someone else smarter than me said:

you can't edit a prefab at runtime you can edit an instantiated prefab but not the prefab itself

So I think the general idea will still work but just might need to be a little more complicated.

Well, never mind the never mind...more investigation and experiments are needed! There's gonna be a ton of "it depends" attached to this.