GC Alloc in DSP.getMeteringInfo

I’m noticing that there is GC allocation when calling DSP.getMeteringInfo, as shown in the (deep) profile below:

I’ve cached the variables that I can on my side, and so the code is simply just this now:

void LateUpdate() {
  m_VoiceEventInstanceDSP.getMeteringInfo(out inputInfo, IntPtr.Zero);
}

For something that is typically run frequently, it would be great if this could be made to not allocate.

Thanks for pointing the allocation out, I can definitely see what you mean regarding the frequency the method is usually called with. I’ve added your suggestion to our internal improvement tracker.

1 Like

Thanks, that would be great as we are hoping to go to production with our title before long.

1 Like

Just to follow up on this, if you’re willing to modify the FMOD Unity integration code, you can swap usages of the out keyword for ref for DSP.getMeteringInfo(), and it should remove the GC allocation. You’d also need to make sure to initialize any DSP_METERING_INFO structs with new FMOD.DSP_METERING_INFO() since you’re passing them to the function instead of having them passed out.

For just the code in the FMOD Unity integration (exact lines may differ based on your version), you’d need to modify the following:

  • fmod.cs, lines ~3434 - ~3444: change any usages of out to ref for all getMeteringInfo overloads
  • RuntimeManager.cs, lines ~721 - ~722: add struct initialization and change keyword usage
  • EditorUtils.cs, lines ~816 - ~817: add struct initialization and change keyword usage

Thanks for the update! I am assuming that these improvements will make it in to an official update? We regularly update FMOD to the latest version so this would be a bit cumbersome to remember to apply every time.

The improvements are scheduled to be included in an official update, yes. However, since we aim to maintain code compatibility between minor API versions, it’s unlikely to be until our next major version update (2.04) since the keyword change would necessitate code changes on the part of people using DSP_METERING_INFO structs in their own code.

So I tried your suggestions but still get the GC as seen here:

This is a patch file showing what changes I made.

diff --git a/Assets/Plugins/FMOD/src/Editor/EditorUtils.cs b/Assets/Plugins/FMOD/src/Editor/EditorUtils.cs
index bdcffac7a..da2982370 100644
--- a/Assets/Plugins/FMOD/src/Editor/EditorUtils.cs
+++ b/Assets/Plugins/FMOD/src/Editor/EditorUtils.cs
@@ -813,8 +813,8 @@ public static float[] GetMetering()
             FMOD.DSP masterHead;
             CheckResult(master.getDSP(FMOD.CHANNELCONTROL_DSP_INDEX.HEAD, out masterHead));
 
-            FMOD.DSP_METERING_INFO outputMetering;
-            CheckResult(masterHead.getMeteringInfo(IntPtr.Zero, out outputMetering));
+            FMOD.DSP_METERING_INFO outputMetering = new();
+            CheckResult(masterHead.getMeteringInfo(IntPtr.Zero, ref outputMetering));
 
             FMOD.SPEAKERMODE mode;
             int rate, raw;
diff --git a/Assets/Plugins/FMOD/src/RuntimeManager.cs b/Assets/Plugins/FMOD/src/RuntimeManager.cs
index b3e5f6a83..921d1437a 100644
--- a/Assets/Plugins/FMOD/src/RuntimeManager.cs
+++ b/Assets/Plugins/FMOD/src/RuntimeManager.cs
@@ -718,8 +718,8 @@ private void UpdateDebugText()
                     coreSystem.getChannelsPlaying(out channels, out realchannels);
                     debug.AppendFormat("CHANNELS: real = {0}, total = {1}\n", realchannels, channels);
 
-                    FMOD.DSP_METERING_INFO outputMetering;
-                    mixerHead.getMeteringInfo(IntPtr.Zero, out outputMetering);
+                    FMOD.DSP_METERING_INFO outputMetering = new();
+                    mixerHead.getMeteringInfo(IntPtr.Zero, ref outputMetering);
                     float rms = 0;
                     for (int i = 0; i < outputMetering.numchannels; i++)
                     {
diff --git a/Assets/Plugins/FMOD/src/fmod.cs b/Assets/Plugins/FMOD/src/fmod.cs
index 0ab3b032a..c45d312db 100644
--- a/Assets/Plugins/FMOD/src/fmod.cs
+++ b/Assets/Plugins/FMOD/src/fmod.cs
@@ -3440,17 +3440,17 @@ public RESULT getMeteringEnabled(out bool inputEnabled, out bool outputEnabled)
             return FMOD5_DSP_GetMeteringEnabled(this.handle, out inputEnabled, out outputEnabled);
         }
 
-        public RESULT getMeteringInfo(IntPtr zero, out DSP_METERING_INFO outputInfo)
+        public RESULT getMeteringInfo(IntPtr zero, ref DSP_METERING_INFO outputInfo)
         {
-            return FMOD5_DSP_GetMeteringInfo(this.handle, zero, out outputInfo);
+            return FMOD5_DSP_GetMeteringInfo(this.handle, zero, ref outputInfo);
         }
-        public RESULT getMeteringInfo(out DSP_METERING_INFO inputInfo, IntPtr zero)
+        public RESULT getMeteringInfo(ref DSP_METERING_INFO inputInfo, IntPtr zero)
         {
-            return FMOD5_DSP_GetMeteringInfo(this.handle, out inputInfo, zero);
+            return FMOD5_DSP_GetMeteringInfo(this.handle, ref inputInfo, zero);
         }
-        public RESULT getMeteringInfo(out DSP_METERING_INFO inputInfo, out DSP_METERING_INFO outputInfo)
+        public RESULT getMeteringInfo(ref DSP_METERING_INFO inputInfo, ref DSP_METERING_INFO outputInfo)
         {
-            return FMOD5_DSP_GetMeteringInfo(this.handle, out inputInfo, out outputInfo);
+            return FMOD5_DSP_GetMeteringInfo(this.handle, ref inputInfo, ref outputInfo);
         }
 
         public RESULT getCPUUsage(out uint exclusive, out uint inclusive)
@@ -3540,11 +3540,11 @@ public RESULT getCPUUsage(out uint exclusive, out uint inclusive)
         [DllImport(VERSION.dll)]
         public static extern RESULT FMOD5_DSP_GetMeteringEnabled         (IntPtr dsp, out bool inputEnabled, out bool outputEnabled);
         [DllImport(VERSION.dll)]
-        public static extern RESULT FMOD5_DSP_GetMeteringInfo            (IntPtr dsp, IntPtr zero, out DSP_METERING_INFO outputInfo);
+        public static extern RESULT FMOD5_DSP_GetMeteringInfo            (IntPtr dsp, IntPtr zero, ref DSP_METERING_INFO outputInfo);
         [DllImport(VERSION.dll)]
-        public static extern RESULT FMOD5_DSP_GetMeteringInfo            (IntPtr dsp, out DSP_METERING_INFO inputInfo, IntPtr zero);
+        public static extern RESULT FMOD5_DSP_GetMeteringInfo            (IntPtr dsp, ref DSP_METERING_INFO inputInfo, IntPtr zero);
         [DllImport(VERSION.dll)]
-        public static extern RESULT FMOD5_DSP_GetMeteringInfo            (IntPtr dsp, out DSP_METERING_INFO inputInfo, out DSP_METERING_INFO outputInfo);
+        public static extern RESULT FMOD5_DSP_GetMeteringInfo            (IntPtr dsp, ref DSP_METERING_INFO inputInfo, ref DSP_METERING_INFO outputInfo);
         [DllImport(VERSION.dll)]
         public static extern RESULT FMOD5_DSP_GetCPUUsage                (IntPtr dsp, out uint exclusive, out uint inclusive);
         #endregion
diff --git a/Assets/Scripts/Player/PlayerVoiceSpeaker.cs b/Assets/Scripts/Player/PlayerVoiceSpeaker.cs
index 05bd2f60d..b2a49828c 100644
--- a/Assets/Scripts/Player/PlayerVoiceSpeaker.cs
+++ b/Assets/Scripts/Player/PlayerVoiceSpeaker.cs
@@ -26,7 +26,7 @@ public class PlayerVoiceSpeaker : Speaker {
     PARAMETER_ID m_OcclusionParameter;
     IAudioOut<float> m_VoiceEvent;
     Transform m_Head;
-    DSP_METERING_INFO inputInfo;
+    DSP_METERING_INFO inputInfo = new();
 
     protected override void Awake() {
         base.Awake();
@@ -75,7 +75,7 @@ public class PlayerVoiceSpeaker : Speaker {
             }
         }
 
-        m_VoiceEventInstanceDSP.getMeteringInfo(out inputInfo, IntPtr.Zero);
+        m_VoiceEventInstanceDSP.getMeteringInfo(ref inputInfo, IntPtr.Zero);
         VoiceEventDecibels = FMODUtility.lin2dB(inputInfo.rmslevel[0]);
 
         // OcclusionAudioSource on the Player only sets Occlusion for child StudioEventEmitter components

Separately, if you ever wanted to release it in a minor API version update you could add a new function override for this non-GC alternative and add the Obsolete attribute to the old ones.

My apologies - the issue is a little more complex than just changing the keyword, and has to do with the whether or not members of the struct are blittable. Unfortunately I don’t have a workaround for you at the moment, but I’ll update you when I do.

I will note this on our feature tracker; I can’t make any promises, but it may be possible depending on the implementation of the fix.