From 3bd396f515f8a539274da110b810fae376d86d80 Mon Sep 17 00:00:00 2001 From: Phantomical Date: Fri, 7 Nov 2025 23:30:28 -0800 Subject: [PATCH] perf: Optimize RSE.LateUpdate by patching AudioSource ctor Every frame, RocketSoundEnhancement makes a FindObjectsOfType call in order to discover any new AudioSources that have been created in the last frame. This is slow. This commit does away with that by: - Patching AudioSource::.ctor to add the newly created AudioSource object to a list (if there is currently an instance of RocketSoundEnhancement active). - Processing the new items in the list in LateUpdate instead of checking all available items. There might be some further simplifications that can be done here now that we know that we're only processing new AudioSources. None of them would have an effect on performance though, so I have left them in just to be safe. In my testing this change reduces the time spent in RSE.LateUpdate from 4.4s to 90ms over a 253s launch to orbit. --- .github/workflows/build.yml | 2 + .../Patches/AudioSource_Patch.cs | 17 +++++++++ .../RocketSoundEnhancement.cs | 32 ++++++++++++---- .../RocketSoundEnhancement.csproj | 38 +++++++++++-------- Source/RocketSoundEnhancement/Startup.cs | 11 +++--- 5 files changed, 72 insertions(+), 28 deletions(-) create mode 100644 Source/RocketSoundEnhancement/Patches/AudioSource_Patch.cs diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 48b7a2f..49adea2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,3 +14,5 @@ on: jobs: build: uses: KSPModdingLibs/KSPBuildTools/.github/workflows/build.yml@0.0.4 + with: + dependency-identifiers: Harmony2 diff --git a/Source/RocketSoundEnhancement/Patches/AudioSource_Patch.cs b/Source/RocketSoundEnhancement/Patches/AudioSource_Patch.cs new file mode 100644 index 0000000..9f00d37 --- /dev/null +++ b/Source/RocketSoundEnhancement/Patches/AudioSource_Patch.cs @@ -0,0 +1,17 @@ +using HarmonyLib; +using UnityEngine; +using System; + +namespace RocketSoundEnhancement.Patches +{ + [HarmonyPatch(typeof(AudioSource), MethodType.Constructor, argumentTypes: new Type[0])] + internal static class AudioSource_Ctor_Patch + { + static void Postfix(AudioSource __instance) + { + RocketSoundEnhancement.Instance?.newAudioSources?.Add(__instance); + } + } +} + + diff --git a/Source/RocketSoundEnhancement/RocketSoundEnhancement.cs b/Source/RocketSoundEnhancement/RocketSoundEnhancement.cs index 1080cf1..161035c 100644 --- a/Source/RocketSoundEnhancement/RocketSoundEnhancement.cs +++ b/Source/RocketSoundEnhancement/RocketSoundEnhancement.cs @@ -58,6 +58,7 @@ public static AudioMixer Mixer private float lastInteriorCutoffFreq; private HashSet managedSources = new HashSet(); + internal readonly List newAudioSources = new List(); private bool gamePaused; @@ -65,6 +66,8 @@ private void Awake() { if (instance != null) { Destroy(instance); } + // Get all the currently existing audio sources. + newAudioSources.AddRange((AudioSource[])FindObjectsOfType(typeof(AudioSource))); instance = this; } @@ -162,27 +165,42 @@ public float WindModulation() return Mathf.Lerp(1, windModulation, Mathf.Clamp01((float)FlightGlobals.ActiveVessel.atmDensity)); } + bool GetNextAudioSource(out AudioSource source) + { + if (newAudioSources.Count == 0) + { + source = null; + return false; + } + + // This makes sure things still work even if a new audio source ends + // up being created while we are running. + source = newAudioSources[newAudioSources.Count - 1]; + newAudioSources.RemoveAt(newAudioSources.Count - 1); + return true; + } + public void LateUpdate() { if (gamePaused || !HighLogic.LoadedSceneIsFlight) return; if (!Settings.EnableAudioEffects || Mixer == null) { - // TODO: some kind of latch so this only runs once - foreach (var sourceObj in FindObjectsOfType(typeof(AudioSource))) + while (GetNextAudioSource(out var source)) { - AudioSource source = (AudioSource)sourceObj; - if (source != null) source.outputAudioMixerGroup = null; + if (source != null) + source.outputAudioMixerGroup = null; } + managedSources.Clear(); - return; } // NOTE: the generic (strongly typed) version of FindObjectsOfType is slower! - foreach (var sourceObj in FindObjectsOfType(typeof(AudioSource))) + while (GetNextAudioSource(out var source)) { - AudioSource source = (AudioSource)sourceObj; + if (source == null) + continue; int instanceID = source.GetInstanceID(); diff --git a/Source/RocketSoundEnhancement/RocketSoundEnhancement.csproj b/Source/RocketSoundEnhancement/RocketSoundEnhancement.csproj index 05ab5d8..c56fb8e 100644 --- a/Source/RocketSoundEnhancement/RocketSoundEnhancement.csproj +++ b/Source/RocketSoundEnhancement/RocketSoundEnhancement.csproj @@ -7,26 +7,34 @@ - - GameData\RocketSoundEnhancement\Plugins - true - true - + + GameData\RocketSoundEnhancement\Plugins + true + true + - - - {5dff0176-7ba8-49f4-8155-f5d9b37d7d3d} - RocketSoundEnhancement.Unity - False - - + + + {5dff0176-7ba8-49f4-8155-f5d9b37d7d3d} + RocketSoundEnhancement.Unity + False + + - all - runtime; build; native; contentfiles; analyzers; buildtransitive + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + $(KSPRoot)/GameData/000_Harmony/0Harmony.dll + false + HarmonyKSP + 2.2.1 + - + \ No newline at end of file diff --git a/Source/RocketSoundEnhancement/Startup.cs b/Source/RocketSoundEnhancement/Startup.cs index f6012b2..33e24bc 100644 --- a/Source/RocketSoundEnhancement/Startup.cs +++ b/Source/RocketSoundEnhancement/Startup.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using HarmonyLib; using UnityEngine; namespace RocketSoundEnhancement @@ -15,7 +11,7 @@ void Awake() AudioConfiguration audioConfig = AudioSettings.GetConfiguration(); audioConfig.numRealVoices = Settings.VoiceCount; - if(AudioSettings.Reset(audioConfig)) { + if (AudioSettings.Reset(audioConfig)) { Debug.Log("[RSE]: Audio Settings Applied"); Debug.Log("[RSE]: DSP Buffer Size : " + AudioSettings.GetConfiguration().dspBufferSize); Debug.Log("[RSE]: Real Voices : " + AudioSettings.GetConfiguration().numRealVoices); @@ -23,6 +19,9 @@ void Awake() Debug.Log("[RSE]: Samplerate : " + AudioSettings.GetConfiguration().sampleRate); Debug.Log("[RSE]: Spearker Mode : " + AudioSettings.GetConfiguration().speakerMode); } + + Harmony harmony = new Harmony("RocketSoundEnhancement"); + harmony.PatchAll(typeof(Startup).Assembly); } } }