Skip to content

Commit 1caf29a

Browse files
fix: changing SceneEventData allocator from TmpJob to Persistent (#1237)
* fix this removes the memory leak warning where the InternalBuffer reader was being created with Allocator.TmpJob (if around for more than 4 frames it generates a warning). Switch this to be Allocator.Persistent since loading and scene synchronization will most likely happen over more than a 4 frame period of time. * test first half. * test Backing out scenes loaded for MultiSceneSynchronizationTest * fix and test While writing the test for this PR I found an issue where it could try to dispose SceneEventData instance more than once during a MultiInstance unit test. Removing the EndSceneEvent(sceneEventId); from line 998 fixed this issue. The rest of this commit finalizes the test for this PR. * style replaced UNITY_EDITOR || DEVELOPMENT_BUILD with UNITY_INCLUDE_TESTS * refactor Created a better test to verify that changing the FastBufferReader's memory allocation type from Allocator.TmpJob to Allocator.Persistent fixes the issue where a scene event could take longer than 4 frames and cause an error and log that a potential memory leak could be happening. Removed previous test and associated changes to NetworkSceneManager. * refactor missed one #if UNITY_INCLUDE_TESTS
1 parent af827f3 commit 1caf29a

File tree

3 files changed

+64
-7
lines changed

3 files changed

+64
-7
lines changed

com.unity.netcode.gameobjects/Runtime/SceneManagement/NetworkSceneManager.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ private void OnClientUnloadScene(uint sceneEventId)
770770
}
771771
s_IsSceneEventActive = true;
772772
var sceneUnload = (AsyncOperation)null;
773-
#if UNITY_EDITOR || DEVELOPMENT_BUILD
773+
#if UNITY_INCLUDE_TESTS
774774
if (m_IsRunningUnitTest)
775775
{
776776
sceneUnload = new AsyncOperation();
@@ -800,7 +800,7 @@ private void OnClientUnloadScene(uint sceneEventId)
800800
});
801801

802802

803-
#if UNITY_EDITOR || DEVELOPMENT_BUILD
803+
#if UNITY_INCLUDE_TESTS
804804
if (m_IsRunningUnitTest)
805805
{
806806
OnSceneUnloaded(sceneEventId);
@@ -993,10 +993,10 @@ private void OnClientSceneLoadingEvent(uint sceneEventId)
993993
}
994994
else
995995
{
996+
EndSceneEvent(sceneEventId);
996997
throw new Exception($"Could not find the scene handle {sceneEventData.SceneHandle} for scene {sceneName} " +
997998
$"during unit test. Did you forget to register this in the unit test?");
998999
}
999-
EndSceneEvent(sceneEventId);
10001000
return;
10011001
}
10021002
#endif
@@ -1273,7 +1273,7 @@ private void OnClientBeginSync(uint sceneEventId)
12731273
var loadSceneMode = sceneIndex == sceneEventData.SceneIndex ? sceneEventData.LoadSceneMode : LoadSceneMode.Additive;
12741274

12751275
// Always check to see if the scene needs to be validated
1276-
if (!ValidateSceneBeforeLoading(sceneEventData.SceneIndex, loadSceneMode))
1276+
if (!ValidateSceneBeforeLoading(sceneIndex, loadSceneMode))
12771277
{
12781278
EndSceneEvent(sceneEventId);
12791279
return;
@@ -1303,7 +1303,7 @@ private void OnClientBeginSync(uint sceneEventId)
13031303
shouldPassThrough = true;
13041304
}
13051305

1306-
#if UNITY_EDITOR || DEVELOPMENT_BUILD
1306+
#if UNITY_INCLUDE_TESTS
13071307
if (m_IsRunningUnitTest)
13081308
{
13091309
// In unit tests, we don't allow clients to load additional scenes since

com.unity.netcode.gameobjects/Runtime/SceneManagement/SceneEventData.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,8 @@ internal void Deserialize(FastBufferReader reader)
475475
// We store off the trailing in-scene placed serialized NetworkObject data to
476476
// be processed once we are done loading.
477477
m_HasInternalBuffer = true;
478-
InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, reader.Length - reader.Position);
478+
// We use Allocator.Persistent since scene loading could take longer than 4 frames
479+
InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.Persistent, reader.Length - reader.Position);
479480
}
480481
break;
481482
}
@@ -517,7 +518,8 @@ internal void CopySceneSyncrhonizationData(FastBufferReader reader)
517518
}
518519

519520
m_HasInternalBuffer = true;
520-
InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.TempJob, sizeToCopy);
521+
// We use Allocator.Persistent since scene synchronization will most likely take longer than 4 frames
522+
InternalBuffer = new FastBufferReader(reader.GetUnsafePtrAtCurrentPosition(), Allocator.Persistent, sizeToCopy);
521523
}
522524
}
523525

testproject/Assets/Tests/Runtime/NetworkSceneManagerTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,4 +546,59 @@ public IEnumerator SceneEventDataPoolSceneLoadingTest([Values(LoadSceneMode.Sing
546546
yield break;
547547
}
548548
}
549+
550+
/// <summary>
551+
/// This is where all of the SceneEventData specific tests should reside.
552+
/// </summary>
553+
public class SceneEventDataTests
554+
{
555+
/// <summary>
556+
/// This verifies that change from Allocator.TmpJob to Allocator.Persistent
557+
/// will not cause memory leak warning notifications if the scene event takes
558+
/// longer than 4 frames to complete.
559+
/// </summary>
560+
/// <returns></returns>
561+
[UnityTest]
562+
public IEnumerator FastReaderAllocationTest()
563+
{
564+
var fastBufferWriter = new FastBufferWriter(1024, Unity.Collections.Allocator.Persistent);
565+
var networkManagerGameObject = new GameObject("NetworkManager - Host");
566+
567+
var networkManager = networkManagerGameObject.AddComponent<NetworkManager>();
568+
networkManager.NetworkConfig = new NetworkConfig()
569+
{
570+
ConnectionApproval = false,
571+
NetworkPrefabs = new List<NetworkPrefab>(),
572+
NetworkTransport = networkManagerGameObject.AddComponent<SIPTransport>(),
573+
};
574+
575+
networkManager.StartHost();
576+
577+
var sceneEventData = new SceneEventData(networkManager);
578+
sceneEventData.SceneEventType = SceneEventData.SceneEventTypes.S2C_Load;
579+
sceneEventData.SceneIndex = 0;
580+
sceneEventData.SceneEventProgressId = Guid.NewGuid();
581+
sceneEventData.LoadSceneMode = LoadSceneMode.Single;
582+
sceneEventData.SceneHandle = 32768;
583+
584+
sceneEventData.Serialize(fastBufferWriter);
585+
var nativeArray = new Unity.Collections.NativeArray<byte>(fastBufferWriter.ToArray(), Unity.Collections.Allocator.Persistent);
586+
var fastBufferReader = new FastBufferReader(nativeArray, Unity.Collections.Allocator.Persistent, fastBufferWriter.ToArray().Length);
587+
588+
var incomingSceneEventData = new SceneEventData(networkManager);
589+
incomingSceneEventData.Deserialize(fastBufferReader);
590+
591+
// Wait for 30 frames
592+
var framesToWait = Time.frameCount + 30;
593+
yield return new WaitUntil(() => Time.frameCount > framesToWait);
594+
595+
// As long as no errors occurred, the test verifies that
596+
incomingSceneEventData.Dispose();
597+
fastBufferReader.Dispose();
598+
nativeArray.Dispose();
599+
fastBufferWriter.Dispose();
600+
networkManager.Shutdown();
601+
UnityEngine.Object.Destroy(networkManagerGameObject);
602+
}
603+
}
549604
}

0 commit comments

Comments
 (0)