fr.jeanf.scenemanagement 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Runtime/AdditiveLoading/SceneLoader.cs +89 -15
- package/Runtime/AdditiveLoading/WorldManager.cs +43 -16
- package/Runtime/DynamicLoading/Editor/VolumeDataGenerator.cs +360 -0
- package/Runtime/DynamicLoading/Editor/VolumeDataGenerator.cs.meta +2 -0
- package/Runtime/DynamicLoading/PrecomputedVolumeData.cs +123 -0
- package/Runtime/DynamicLoading/PrecomputedVolumeData.cs.meta +2 -0
- package/Runtime/DynamicLoading/RegionConnectivityAuthoring.cs +101 -5
- package/Runtime/DynamicLoading/VolumeSystem.cs +145 -117
- package/Samples/Example/PrecomputedVolumeData.asset +360 -0
- package/Samples/Example/PrecomputedVolumeData.asset.meta +8 -0
- package/Samples/Example/Scenes/Main.unity +0 -289
- package/Samples/Example/Scenes/World.unity +2 -0
- package/package.json +1 -1
|
@@ -12,7 +12,10 @@ namespace jeanf.scenemanagement
|
|
|
12
12
|
{
|
|
13
13
|
public bool isDebug = false;
|
|
14
14
|
private CancellationTokenSource _queueCts;
|
|
15
|
-
[SerializeField] private int maxConcurrentLoads = 2;
|
|
15
|
+
[SerializeField] private int maxConcurrentLoads = 2;
|
|
16
|
+
[SerializeField] private int gcFrameSpread = 5;
|
|
17
|
+
[SerializeField] private bool enableIncrementalGC = true;
|
|
18
|
+
[SerializeField] private float memoryFlushDelay = 0.1f;
|
|
16
19
|
|
|
17
20
|
public delegate void IsLoadingDelegate(bool loadingState);
|
|
18
21
|
public static IsLoadingDelegate IsLoading;
|
|
@@ -49,6 +52,7 @@ namespace jeanf.scenemanagement
|
|
|
49
52
|
|
|
50
53
|
private bool _isProcessingLoadQueue = false;
|
|
51
54
|
private bool _isProcessingUnloadQueue = false;
|
|
55
|
+
private bool _isFlushingMemory = false;
|
|
52
56
|
|
|
53
57
|
private void OnEnable() => Subscribe();
|
|
54
58
|
private void OnDisable() => Unsubscribe();
|
|
@@ -59,6 +63,7 @@ namespace jeanf.scenemanagement
|
|
|
59
63
|
LoadSceneRequest += QueueLoadScene;
|
|
60
64
|
UnLoadSceneRequest += QueueUnloadScene;
|
|
61
65
|
UnloadAllScenesRequest += QueueUnloadAllScenes;
|
|
66
|
+
FlushScenesRequest += () => IncrementalMemoryFlush().Forget();
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
private void Unsubscribe()
|
|
@@ -66,6 +71,7 @@ namespace jeanf.scenemanagement
|
|
|
66
71
|
LoadSceneRequest -= QueueLoadScene;
|
|
67
72
|
UnLoadSceneRequest -= QueueUnloadScene;
|
|
68
73
|
UnloadAllScenesRequest -= QueueUnloadAllScenes;
|
|
74
|
+
FlushScenesRequest = null;
|
|
69
75
|
}
|
|
70
76
|
|
|
71
77
|
private enum SceneOperationType { Load, Unload }
|
|
@@ -84,8 +90,6 @@ namespace jeanf.scenemanagement
|
|
|
84
90
|
|
|
85
91
|
private void QueueUnloadAllScenes()
|
|
86
92
|
{
|
|
87
|
-
if (isDebug) Debug.Log("Unloading all scenes");
|
|
88
|
-
|
|
89
93
|
while (_loadQueue.TryDequeue(out _)) { }
|
|
90
94
|
|
|
91
95
|
_scenesToUnload.Clear();
|
|
@@ -102,14 +106,48 @@ namespace jeanf.scenemanagement
|
|
|
102
106
|
ProcessUnloadQueue().Forget();
|
|
103
107
|
}
|
|
104
108
|
|
|
105
|
-
private async
|
|
109
|
+
private async UniTaskVoid IncrementalMemoryFlush()
|
|
106
110
|
{
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
111
|
+
if (_isFlushingMemory) return;
|
|
112
|
+
_isFlushingMemory = true;
|
|
113
|
+
|
|
114
|
+
try
|
|
115
|
+
{
|
|
116
|
+
await UniTask.Delay(TimeSpan.FromSeconds(memoryFlushDelay), DelayType.Realtime);
|
|
117
|
+
|
|
118
|
+
var unloadOperation = Resources.UnloadUnusedAssets();
|
|
119
|
+
await unloadOperation.ToUniTask();
|
|
120
|
+
|
|
121
|
+
if (enableIncrementalGC)
|
|
122
|
+
{
|
|
123
|
+
await IncrementalGarbageCollection();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
finally
|
|
127
|
+
{
|
|
128
|
+
_isFlushingMemory = false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private async UniTask IncrementalGarbageCollection()
|
|
133
|
+
{
|
|
134
|
+
if (GC.MaxGeneration >= 2)
|
|
135
|
+
{
|
|
136
|
+
for (int generation = 0; generation <= GC.MaxGeneration; generation++)
|
|
137
|
+
{
|
|
138
|
+
GC.Collect(generation, GCCollectionMode.Optimized, false);
|
|
139
|
+
|
|
140
|
+
for (int frame = 0; frame < gcFrameSpread; frame++)
|
|
141
|
+
{
|
|
142
|
+
await UniTask.Yield();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else
|
|
147
|
+
{
|
|
148
|
+
GC.Collect(0, GCCollectionMode.Optimized, false);
|
|
149
|
+
await UniTask.Yield();
|
|
150
|
+
}
|
|
113
151
|
}
|
|
114
152
|
|
|
115
153
|
private async UniTaskVoid ProcessLoadQueue()
|
|
@@ -191,7 +229,6 @@ namespace jeanf.scenemanagement
|
|
|
191
229
|
await UniTask.WhenAll(_operationBuffer);
|
|
192
230
|
}
|
|
193
231
|
|
|
194
|
-
// Small delay to prevent overwhelming the system
|
|
195
232
|
await UniTask.Yield();
|
|
196
233
|
}
|
|
197
234
|
}
|
|
@@ -199,7 +236,7 @@ namespace jeanf.scenemanagement
|
|
|
199
236
|
{
|
|
200
237
|
if (_loadedScenes.Count == 0)
|
|
201
238
|
{
|
|
202
|
-
|
|
239
|
+
IncrementalMemoryFlush().Forget();
|
|
203
240
|
}
|
|
204
241
|
|
|
205
242
|
_isProcessingUnloadQueue = false;
|
|
@@ -212,11 +249,9 @@ namespace jeanf.scenemanagement
|
|
|
212
249
|
|
|
213
250
|
private async UniTask LoadSceneAsync(string sceneName, CancellationToken cancellationToken)
|
|
214
251
|
{
|
|
215
|
-
AsyncOperation loadOperation = null;
|
|
216
|
-
|
|
217
252
|
try
|
|
218
253
|
{
|
|
219
|
-
loadOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
|
|
254
|
+
var loadOperation = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive);
|
|
220
255
|
loadOperation.allowSceneActivation = true;
|
|
221
256
|
await loadOperation.ToUniTask(cancellationToken: cancellationToken);
|
|
222
257
|
_loadedScenes.Add(sceneName);
|
|
@@ -240,5 +275,44 @@ namespace jeanf.scenemanagement
|
|
|
240
275
|
_processingScenes.Remove(sceneName);
|
|
241
276
|
}
|
|
242
277
|
}
|
|
278
|
+
|
|
279
|
+
private void OnApplicationFocus(bool hasFocus)
|
|
280
|
+
{
|
|
281
|
+
if (!hasFocus && enableIncrementalGC)
|
|
282
|
+
{
|
|
283
|
+
IncrementalMemoryFlush().Forget();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private void OnApplicationPause(bool pauseStatus)
|
|
288
|
+
{
|
|
289
|
+
if (pauseStatus && enableIncrementalGC)
|
|
290
|
+
{
|
|
291
|
+
IncrementalMemoryFlush().Forget();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
public void ForceMemoryFlush()
|
|
296
|
+
{
|
|
297
|
+
if (!_isFlushingMemory)
|
|
298
|
+
{
|
|
299
|
+
IncrementalMemoryFlush().Forget();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
public bool IsCurrentlyLoading()
|
|
304
|
+
{
|
|
305
|
+
return _isProcessingLoadQueue || _isProcessingUnloadQueue || _isFlushingMemory;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
public int GetLoadedSceneCount()
|
|
309
|
+
{
|
|
310
|
+
return _loadedScenes.Count;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
public int GetPendingOperationCount()
|
|
314
|
+
{
|
|
315
|
+
return _loadQueue.Count + _unloadQueue.Count;
|
|
316
|
+
}
|
|
243
317
|
}
|
|
244
318
|
}
|
|
@@ -24,8 +24,9 @@ namespace jeanf.scenemanagement
|
|
|
24
24
|
private List<Region> _activeRegions = new List<Region>();
|
|
25
25
|
private bool _mappingInitialized = false;
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
private List<
|
|
27
|
+
// GC ALLOCATION FIX: Pre-allocate reusable collections
|
|
28
|
+
private readonly List<string> _tempSceneNames = new List<string>();
|
|
29
|
+
private readonly List<Region> _tempRegionsToRemove = new List<Region>();
|
|
29
30
|
|
|
30
31
|
[SerializeField] private StringEventChannelSO regionChangeRequestChannel;
|
|
31
32
|
[SerializeField] private SendTeleportTarget sendTeleportTarget;
|
|
@@ -126,6 +127,8 @@ namespace jeanf.scenemanagement
|
|
|
126
127
|
_compiledSceneLists.Clear();
|
|
127
128
|
_landingZoneIds.Clear();
|
|
128
129
|
_activeRegions.Clear();
|
|
130
|
+
|
|
131
|
+
// GC ALLOCATION FIX: Clear reusable collections instead of creating new ones
|
|
129
132
|
_tempSceneNames.Clear();
|
|
130
133
|
_tempRegionsToRemove.Clear();
|
|
131
134
|
_mappingInitialized = false;
|
|
@@ -138,8 +141,10 @@ namespace jeanf.scenemanagement
|
|
|
138
141
|
var regionCount = ListOfRegions?.Count ?? 0;
|
|
139
142
|
if (regionCount == 0) return;
|
|
140
143
|
|
|
141
|
-
foreach
|
|
144
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach to avoid enumerator allocation
|
|
145
|
+
for (int i = 0; i < ListOfRegions.Count; i++)
|
|
142
146
|
{
|
|
147
|
+
var region = ListOfRegions[i];
|
|
143
148
|
if (region == null) continue;
|
|
144
149
|
if (!_regionDictionary.TryAdd(region.id, region)) continue;
|
|
145
150
|
|
|
@@ -148,8 +153,10 @@ namespace jeanf.scenemanagement
|
|
|
148
153
|
|
|
149
154
|
if (region.scenariosInThisRegion != null)
|
|
150
155
|
{
|
|
151
|
-
|
|
156
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
157
|
+
for (int j = 0; j < region.scenariosInThisRegion.Count; j++)
|
|
152
158
|
{
|
|
159
|
+
var scenario = region.scenariosInThisRegion[j];
|
|
153
160
|
if (scenario != null)
|
|
154
161
|
{
|
|
155
162
|
ScenarioManager.ScenarioDictionary.TryAdd(scenario.id, scenario);
|
|
@@ -159,8 +166,10 @@ namespace jeanf.scenemanagement
|
|
|
159
166
|
|
|
160
167
|
if (region.zonesInThisRegion != null)
|
|
161
168
|
{
|
|
162
|
-
|
|
169
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
170
|
+
for (int j = 0; j < region.zonesInThisRegion.Count; j++)
|
|
163
171
|
{
|
|
172
|
+
var zone = region.zonesInThisRegion[j];
|
|
164
173
|
if (zone != null)
|
|
165
174
|
{
|
|
166
175
|
_zoneDictionary.TryAdd(zone.id, zone);
|
|
@@ -176,10 +185,13 @@ namespace jeanf.scenemanagement
|
|
|
176
185
|
|
|
177
186
|
private void PrecompileSceneList(Region region)
|
|
178
187
|
{
|
|
188
|
+
// GC ALLOCATION FIX: Use capacity to avoid List resizing
|
|
179
189
|
var sceneNames = new List<string>(region.dependenciesInThisRegion.Count);
|
|
180
|
-
|
|
190
|
+
|
|
191
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
192
|
+
for (int i = 0; i < region.dependenciesInThisRegion.Count; i++)
|
|
181
193
|
{
|
|
182
|
-
sceneNames.Add(
|
|
194
|
+
sceneNames.Add(region.dependenciesInThisRegion[i].SceneName);
|
|
183
195
|
}
|
|
184
196
|
_compiledSceneLists[region.id] = sceneNames;
|
|
185
197
|
}
|
|
@@ -189,8 +201,10 @@ namespace jeanf.scenemanagement
|
|
|
189
201
|
var connectivity = FindObjectOfType<RegionConnectivityAuthoring>();
|
|
190
202
|
if (connectivity?.regionConnectivity?.landingZones == null) return;
|
|
191
203
|
|
|
192
|
-
|
|
204
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
205
|
+
for (int i = 0; i < connectivity.regionConnectivity.landingZones.Count; i++)
|
|
193
206
|
{
|
|
207
|
+
var landing = connectivity.regionConnectivity.landingZones[i];
|
|
194
208
|
if (landing?.landingZone != null)
|
|
195
209
|
{
|
|
196
210
|
_landingZoneIds.Add(landing.landingZone.id);
|
|
@@ -216,6 +230,7 @@ namespace jeanf.scenemanagement
|
|
|
216
230
|
|
|
217
231
|
private void OnZoneChangedFromECS(string zoneId)
|
|
218
232
|
{
|
|
233
|
+
// GC ALLOCATION FIX: Early exit to avoid string operations
|
|
219
234
|
if (string.IsNullOrEmpty(zoneId) || _lastNotifiedZone == zoneId) return;
|
|
220
235
|
|
|
221
236
|
if (!_zoneDictionary.TryGetValue(zoneId, out var zone)) return;
|
|
@@ -223,12 +238,14 @@ namespace jeanf.scenemanagement
|
|
|
223
238
|
_lastNotifiedZone = zoneId;
|
|
224
239
|
_currentPlayerZone = zone;
|
|
225
240
|
|
|
241
|
+
// GC ALLOCATION FIX: Only invoke if delegates are not null
|
|
226
242
|
PublishCurrentZoneId?.Invoke(zone.id);
|
|
227
243
|
PublishAppList(zone);
|
|
228
244
|
}
|
|
229
245
|
|
|
230
246
|
private void OnRegionChangedFromECS(string regionId)
|
|
231
247
|
{
|
|
248
|
+
// GC ALLOCATION FIX: Early exit to avoid unnecessary operations
|
|
232
249
|
if (string.IsNullOrEmpty(regionId) || _lastNotifiedRegion == regionId) return;
|
|
233
250
|
|
|
234
251
|
if (!_regionDictionary.TryGetValue(regionId, out var region)) return;
|
|
@@ -260,18 +277,23 @@ namespace jeanf.scenemanagement
|
|
|
260
277
|
_currentPlayerRegion = region;
|
|
261
278
|
_lastNotifiedRegion = region.id;
|
|
262
279
|
|
|
280
|
+
// GC ALLOCATION FIX: Only invoke if delegate is not null
|
|
263
281
|
PublishCurrentRegionId?.Invoke(_currentPlayerRegion.id);
|
|
264
282
|
|
|
283
|
+
// GC ALLOCATION FIX: Clear and reuse collection instead of creating new
|
|
265
284
|
_tempRegionsToRemove.Clear();
|
|
266
|
-
|
|
285
|
+
|
|
286
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
287
|
+
for (int i = 0; i < _activeRegions.Count; i++)
|
|
267
288
|
{
|
|
268
|
-
var removedRegion = RequestUnLoadForObsoleteRegion(
|
|
289
|
+
var removedRegion = RequestUnLoadForObsoleteRegion(_activeRegions[i]);
|
|
269
290
|
_tempRegionsToRemove.Add(removedRegion);
|
|
270
291
|
}
|
|
271
292
|
|
|
272
|
-
|
|
293
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
294
|
+
for (int i = 0; i < _tempRegionsToRemove.Count; i++)
|
|
273
295
|
{
|
|
274
|
-
_activeRegions.Remove(
|
|
296
|
+
_activeRegions.Remove(_tempRegionsToRemove[i]);
|
|
275
297
|
}
|
|
276
298
|
|
|
277
299
|
RequestLoadForRegionDependencies(region);
|
|
@@ -304,6 +326,8 @@ namespace jeanf.scenemanagement
|
|
|
304
326
|
{
|
|
305
327
|
_currentPlayerZone = firstZone;
|
|
306
328
|
_lastNotifiedZone = firstZone.id;
|
|
329
|
+
|
|
330
|
+
// GC ALLOCATION FIX: Only invoke if delegates are not null
|
|
307
331
|
PublishCurrentZoneId?.Invoke(firstZone.id);
|
|
308
332
|
PublishAppList(firstZone);
|
|
309
333
|
}
|
|
@@ -330,6 +354,7 @@ namespace jeanf.scenemanagement
|
|
|
330
354
|
listToBroadcast = value;
|
|
331
355
|
}
|
|
332
356
|
|
|
357
|
+
// GC ALLOCATION FIX: Only invoke if delegate is not null
|
|
333
358
|
_broadcastAppList?.Invoke(listToBroadcast);
|
|
334
359
|
}
|
|
335
360
|
|
|
@@ -337,9 +362,10 @@ namespace jeanf.scenemanagement
|
|
|
337
362
|
{
|
|
338
363
|
if (!_compiledSceneLists.TryGetValue(region.id, out var sceneNames) || sceneNames.Count <= 0) return;
|
|
339
364
|
|
|
340
|
-
|
|
365
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
366
|
+
for (int i = 0; i < sceneNames.Count; i++)
|
|
341
367
|
{
|
|
342
|
-
_sceneLoader.LoadSceneRequest(
|
|
368
|
+
_sceneLoader.LoadSceneRequest(sceneNames[i]);
|
|
343
369
|
}
|
|
344
370
|
}
|
|
345
371
|
|
|
@@ -347,9 +373,10 @@ namespace jeanf.scenemanagement
|
|
|
347
373
|
{
|
|
348
374
|
if (_compiledSceneLists.TryGetValue(region.id, out var sceneNames))
|
|
349
375
|
{
|
|
350
|
-
|
|
376
|
+
// GC ALLOCATION FIX: Use for loop instead of foreach
|
|
377
|
+
for (int i = 0; i < sceneNames.Count; i++)
|
|
351
378
|
{
|
|
352
|
-
_sceneLoader.UnLoadSceneRequest(
|
|
379
|
+
_sceneLoader.UnLoadSceneRequest(sceneNames[i]);
|
|
353
380
|
}
|
|
354
381
|
}
|
|
355
382
|
return region;
|