From 26a7dc41a260a0fc95bd61f1eedb34dfe86455a0 Mon Sep 17 00:00:00 2001 From: kazuki Date: Tue, 26 Sep 2023 17:30:00 +0900 Subject: [PATCH 1/9] GCAlloc --- .../ExecutableUnitySynchronizationContext.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/Internal/ExecutableUnitySynchronizationContext.cs b/Runtime/Scripts/Internal/ExecutableUnitySynchronizationContext.cs index 4e73816328..3c60230b62 100644 --- a/Runtime/Scripts/Internal/ExecutableUnitySynchronizationContext.cs +++ b/Runtime/Scripts/Internal/ExecutableUnitySynchronizationContext.cs @@ -19,6 +19,8 @@ class ExecutableUnitySynchronizationContext : SynchronizationContext const int k_AwqInitialCapacity = 20; static SynchronizationContext s_MainThreadContext; + static object s_CallbackObject; + static SendOrPostCallback s_CallbackMethod; readonly List m_AsyncWorkQueue; readonly List m_CurrentFrameWork = new List(k_AwqInitialCapacity); @@ -32,6 +34,16 @@ internal ExecutableUnitySynchronizationContext(SynchronizationContext context) s_MainThreadContext = context; } + if (s_CallbackObject == null) + { + s_CallbackObject = new CallbackObject(ExecuteAndAppendNextExecute); + } + + if (s_CallbackMethod == null) + { + s_CallbackMethod = SendOrPostCallback; + } + if (s_MainThreadContext == null || s_MainThreadContext != context) { throw new InvalidOperationException("Unable to create executable synchronization context without a valid synchronization context."); @@ -42,7 +54,7 @@ internal ExecutableUnitySynchronizationContext(SynchronizationContext context) m_AsyncWorkQueue = new List(); // Queue up and Execute work request with the synchronization context. - s_MainThreadContext.Post(SendOrPostCallback, new CallbackObject(ExecuteAndAppendNextExecute)); + s_MainThreadContext.Post(s_CallbackMethod, s_CallbackObject); } ExecutableUnitySynchronizationContext(List queue, int mainThreadID) @@ -191,7 +203,7 @@ void ExecuteAndAppendNextExecute() // UnitySynchronizationContext works by performing work in batches so as not to stall the main thread // forever. Therefore it is safe to re-add ourselves to the delayed work queue. This is how we hook into // the main thread delayed tasks. - s_MainThreadContext.Post(SendOrPostCallback, new CallbackObject(ExecuteAndAppendNextExecute)); + s_MainThreadContext.Post(s_CallbackMethod, s_CallbackObject); } class CallbackObject From 4cf334a2dcdec134dae41f75fcb03cabaec945f2 Mon Sep 17 00:00:00 2001 From: kazuki Date: Tue, 26 Sep 2023 19:27:21 +0900 Subject: [PATCH 2/9] fix --- Runtime/Scripts/Context.cs | 52 ++++++++++++++-------------------- Runtime/Scripts/WebRTC.cs | 4 +-- Tests/Runtime/NativeAPITest.cs | 14 +++------ 3 files changed, 27 insertions(+), 43 deletions(-) diff --git a/Runtime/Scripts/Context.cs b/Runtime/Scripts/Context.cs index 55c5ca637c..5e8de7c19b 100644 --- a/Runtime/Scripts/Context.cs +++ b/Runtime/Scripts/Context.cs @@ -1,6 +1,8 @@ using System; using System.Runtime.InteropServices; using System.Threading; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine; #if UNITY_EDITOR @@ -61,16 +63,16 @@ internal class Batch public struct BatchData { public int tracksCount; - [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] - public IntPtr[] tracks; + public IntPtr tracks; } - public BatchData data; - public IntPtr ptr; + public NativeArray tracks; + + BatchData data; public Batch() { - ResizeCapacity(1); + tracks = new NativeArray(1, Allocator.Persistent); } ~Batch() @@ -80,40 +82,30 @@ public Batch() public void Dispose() { - if (ptr != IntPtr.Zero) - { - Marshal.FreeHGlobal(ptr); - ptr = IntPtr.Zero; - } + tracks.Dispose(); } public void ResizeCapacity(int totalTracks) { - const int roundedCapacity = 32; - int totalCapacity = ((totalTracks + roundedCapacity) / roundedCapacity) * roundedCapacity; - - if (ptr != IntPtr.Zero && data.tracks.Length >= totalCapacity) - return; - - data.tracksCount = 0; - data.tracks = new IntPtr[totalCapacity]; - - int size = Marshal.SizeOf(typeof(BatchData)) + - Marshal.SizeOf(typeof(IntPtr)) * data.tracks.Length; + tracks.Dispose(); + tracks = new NativeArray(totalTracks, Allocator.Persistent); + } - if (ptr == IntPtr.Zero) - ptr = Marshal.AllocHGlobal(size); - else - ptr = Marshal.ReAllocHGlobal(ptr, (IntPtr)size); - Marshal.StructureToPtr(data, ptr, false); + public unsafe IntPtr GetPtr() + { + data.tracks = new IntPtr(tracks.GetUnsafePtr()); + data.tracksCount = tracks.Length; + fixed (void* ptr = &data) + { + return new IntPtr(ptr); + } } - public void Submit(bool flush = false) + public unsafe void Submit(bool flush = false) { - if (flush == false) + if (!flush) { - Marshal.StructureToPtr(data, ptr, false); - WebRTC.Context.BatchUpdate(ptr); + WebRTC.Context.BatchUpdate(GetPtr()); } else { diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index b5228adad9..1534bec370 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -690,12 +690,10 @@ public static IEnumerator Update() track.UpdateTexture(); if (track.DataPtr != IntPtr.Zero) { - batch.data.tracks[trackIndex] = track.DataPtr; + batch.tracks[trackIndex] = track.DataPtr; trackIndex++; } } - - batch.data.tracksCount = trackIndex; if (trackIndex > 0) batch.Submit(); diff --git a/Tests/Runtime/NativeAPITest.cs b/Tests/Runtime/NativeAPITest.cs index 3b49ff7cdf..9c7791383c 100644 --- a/Tests/Runtime/NativeAPITest.cs +++ b/Tests/Runtime/NativeAPITest.cs @@ -359,13 +359,11 @@ public IEnumerator CallVideoUpdateMethodsEncode() Batch batch = new Batch(); int trackIndex = 0; - batch.data.tracks[trackIndex] = ptr; - batch.data.tracksCount = ++trackIndex; + batch.tracks[trackIndex] = ptr; yield return new WaitForSeconds(1.0f); - Marshal.StructureToPtr(batch.data, batch.ptr, false); - VideoUpdateMethods.BatchUpdate(batchUpdateEvent, batchUpdateEventID, batch.ptr); + VideoUpdateMethods.BatchUpdate(batchUpdateEvent, batchUpdateEventID, batch.GetPtr()); VideoUpdateMethods.Flush(); yield return new WaitForSeconds(1.0f); @@ -414,15 +412,11 @@ public IEnumerator CallVideoUpdateMethodsUpdateRenderer() Batch batch = new Batch(); int trackIndex = 0; - batch.data.tracks[trackIndex] = ptr; - batch.data.tracksCount = ++trackIndex; + batch.tracks[trackIndex] = ptr; yield return new WaitForSeconds(1.0f); - Marshal.StructureToPtr(batch.data, batch.ptr, false); - VideoUpdateMethods.BatchUpdate(batchUpdateEvent, batchUpdateEventID, batch.ptr); - - // this method is not supported on Direct3D12 + VideoUpdateMethods.BatchUpdate(batchUpdateEvent, batchUpdateEventID, batch.GetPtr()); VideoUpdateMethods.UpdateRendererTexture(updateTextureEvent, receiveTexture, rendererId); VideoUpdateMethods.Flush(); From 0804b1c351b3df4715f8abf30ad10c13776d0a95 Mon Sep 17 00:00:00 2001 From: kazuki Date: Wed, 27 Sep 2023 12:03:06 +0900 Subject: [PATCH 3/9] fix gcalloc --- Runtime/Scripts/WebRTC.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index 1534bec370..8b128940af 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -682,9 +682,9 @@ public static IEnumerator Update() batch.ResizeCapacity(VideoStreamTrack.s_tracks.Count); int trackIndex = 0; - foreach (var reference in VideoStreamTrack.s_tracks.Values) + foreach (var pair in VideoStreamTrack.s_tracks) { - if (!reference.TryGetTarget(out var track)) + if (!pair.Value.TryGetTarget(out var track)) continue; track.UpdateTexture(); From 2e6721d1a718596a46e9fc8e16decdb3e53e8dd8 Mon Sep 17 00:00:00 2001 From: kazuki Date: Wed, 27 Sep 2023 12:54:41 +0900 Subject: [PATCH 4/9] fix --- Runtime/Scripts/WebRTC.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index 8b128940af..752d5ab115 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -682,8 +682,10 @@ public static IEnumerator Update() batch.ResizeCapacity(VideoStreamTrack.s_tracks.Count); int trackIndex = 0; - foreach (var pair in VideoStreamTrack.s_tracks) + var enumerator = VideoStreamTrack.s_tracks.GetEnumerator(); + while (enumerator.MoveNext()) { + var pair = enumerator.Current; if (!pair.Value.TryGetTarget(out var track)) continue; From 40e3b804ae8f796be11a96affce3f6a927923ee1 Mon Sep 17 00:00:00 2001 From: kazuki Date: Wed, 27 Sep 2023 15:25:59 +0900 Subject: [PATCH 5/9] remove gcalloc --- Runtime/Scripts/VideoStreamTrack.cs | 27 +++++++++++++++++++-------- Runtime/Scripts/WebRTC.cs | 24 ++++++++++++------------ 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/Runtime/Scripts/VideoStreamTrack.cs b/Runtime/Scripts/VideoStreamTrack.cs index 8c9dd97009..7fc8c9c59c 100644 --- a/Runtime/Scripts/VideoStreamTrack.cs +++ b/Runtime/Scripts/VideoStreamTrack.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Concurrent; +using System.Collections.Generic; using System.ComponentModel; using System.Runtime.InteropServices; using UnityEngine; @@ -30,8 +30,8 @@ public class VideoStreamTrack : MediaStreamTrack /// public static bool NeedReceivedVideoFlipVertically { get; set; } = true; - internal static ConcurrentDictionary> s_tracks = - new ConcurrentDictionary>(); + internal static Dictionary> s_tracks = + new Dictionary>(); internal enum VideoStreamTrackAction { @@ -148,8 +148,12 @@ internal void UpdateTexture() public VideoStreamTrack(Texture texture, CopyTexture copyTexture = null) : base(CreateVideoTrack(texture, out var source)) { - if (!s_tracks.TryAdd(self, new WeakReference(this))) - throw new InvalidOperationException(); + lock(s_tracks) + { + if (s_tracks.ContainsKey(self)) + throw new InvalidOperationException(); + s_tracks.Add(self, new WeakReference(this)); + } m_dataptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VideoStreamTrackData))); Marshal.StructureToPtr(m_data, m_dataptr, false); @@ -170,8 +174,12 @@ public VideoStreamTrack(Texture texture, CopyTexture copyTexture = null) internal VideoStreamTrack(IntPtr ptr) : base(CreateVideoTrack(ptr)) { - if (!s_tracks.TryAdd(self, new WeakReference(this))) - throw new InvalidOperationException(); + lock (s_tracks) + { + if (s_tracks.ContainsKey(self)) + throw new InvalidOperationException(); + s_tracks.Add(self, new WeakReference(this)); + } m_dataptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(VideoStreamTrackData))); Marshal.StructureToPtr(m_data, m_dataptr, false); @@ -205,7 +213,10 @@ public override void Dispose() }, 0.1f); } - s_tracks.TryRemove(self, out var value); + lock(s_tracks) + { + s_tracks.Remove(self); + } } base.Dispose(); } diff --git a/Runtime/Scripts/WebRTC.cs b/Runtime/Scripts/WebRTC.cs index 752d5ab115..94798f0b2c 100644 --- a/Runtime/Scripts/WebRTC.cs +++ b/Runtime/Scripts/WebRTC.cs @@ -679,21 +679,21 @@ public static IEnumerator Update() RenderTexture.active = null; var batch = Context.batch; - batch.ResizeCapacity(VideoStreamTrack.s_tracks.Count); - int trackIndex = 0; - var enumerator = VideoStreamTrack.s_tracks.GetEnumerator(); - while (enumerator.MoveNext()) + lock (VideoStreamTrack.s_tracks) { - var pair = enumerator.Current; - if (!pair.Value.TryGetTarget(out var track)) - continue; - - track.UpdateTexture(); - if (track.DataPtr != IntPtr.Zero) + batch.ResizeCapacity(VideoStreamTrack.s_tracks.Count); + foreach (var pair in VideoStreamTrack.s_tracks) { - batch.tracks[trackIndex] = track.DataPtr; - trackIndex++; + if (!pair.Value.TryGetTarget(out var track)) + continue; + + track.UpdateTexture(); + if (track.DataPtr != IntPtr.Zero) + { + batch.tracks[trackIndex] = track.DataPtr; + trackIndex++; + } } } if (trackIndex > 0) From e21103bfe3a481ccd05324da3bcf6da57710a910 Mon Sep 17 00:00:00 2001 From: kazuki Date: Wed, 27 Sep 2023 15:33:07 +0900 Subject: [PATCH 6/9] format --- Runtime/Scripts/VideoStreamTrack.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Runtime/Scripts/VideoStreamTrack.cs b/Runtime/Scripts/VideoStreamTrack.cs index 7fc8c9c59c..6f391780df 100644 --- a/Runtime/Scripts/VideoStreamTrack.cs +++ b/Runtime/Scripts/VideoStreamTrack.cs @@ -148,7 +148,7 @@ internal void UpdateTexture() public VideoStreamTrack(Texture texture, CopyTexture copyTexture = null) : base(CreateVideoTrack(texture, out var source)) { - lock(s_tracks) + lock (s_tracks) { if (s_tracks.ContainsKey(self)) throw new InvalidOperationException(); @@ -213,7 +213,7 @@ public override void Dispose() }, 0.1f); } - lock(s_tracks) + lock (s_tracks) { s_tracks.Remove(self); } From 08be060af7b1f78e98efc72484d0078b0d4e82f2 Mon Sep 17 00:00:00 2001 From: kazuki Date: Tue, 10 Oct 2023 13:17:29 +0900 Subject: [PATCH 7/9] fix --- Runtime/Scripts/Context.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Runtime/Scripts/Context.cs b/Runtime/Scripts/Context.cs index 5e8de7c19b..15477f83e7 100644 --- a/Runtime/Scripts/Context.cs +++ b/Runtime/Scripts/Context.cs @@ -173,6 +173,7 @@ public void Dispose() // Release buffers on the rendering thread batch.Submit(true); + batch.Dispose(); NativeMethods.ContextDestroy(id); self = IntPtr.Zero; From 17cbc6e2f93c2f81ea29e61d86af4c86a6db4c4d Mon Sep 17 00:00:00 2001 From: kazuki Date: Fri, 13 Oct 2023 14:51:50 +0900 Subject: [PATCH 8/9] fix --- Runtime/Scripts/Context.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Runtime/Scripts/Context.cs b/Runtime/Scripts/Context.cs index 15477f83e7..e7560ee87f 100644 --- a/Runtime/Scripts/Context.cs +++ b/Runtime/Scripts/Context.cs @@ -82,7 +82,11 @@ public Batch() public void Dispose() { - tracks.Dispose(); + if(tracks.IsCreated) + { + tracks.Dispose(); + tracks = default; + } } public void ResizeCapacity(int totalTracks) From 1622ee634707c08d0f6a0321cec0239ea8aba3e6 Mon Sep 17 00:00:00 2001 From: Kazuki Matsumoto <1132081+karasusan@users.noreply.github.com> Date: Fri, 13 Oct 2023 15:56:24 +0900 Subject: [PATCH 9/9] Update Context.cs --- Runtime/Scripts/Context.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Runtime/Scripts/Context.cs b/Runtime/Scripts/Context.cs index e7560ee87f..a4b141d0db 100644 --- a/Runtime/Scripts/Context.cs +++ b/Runtime/Scripts/Context.cs @@ -82,7 +82,7 @@ public Batch() public void Dispose() { - if(tracks.IsCreated) + if (tracks.IsCreated) { tracks.Dispose(); tracks = default;