213 lines
6.9 KiB
C#

using System;
using System.Threading;
#if UNITY_2019_3_OR_NEWER
using UnityEngine.LowLevel;
using UnityEngine.PlayerLoop;
#else
using UnityEngine.Experimental.LowLevel;
using UnityEngine.Experimental.PlayerLoop;
#endif
namespace VContainer.Unity
{
public struct VContainerInitialization {}
public struct VContainerPostInitialization {}
public struct VContainerStartup {}
public struct VContainerPostStartup {}
public struct VContainerFixedUpdate {}
public struct VContainerPostFixedUpdate {}
public struct VContainerUpdate {}
public struct VContainerPostUpdate {}
public struct VContainerLateUpdate {}
public struct VContainerPostLateUpdate {}
enum PlayerLoopTiming
{
Initialization = 0,
PostInitialization = 1,
Startup = 2,
PostStartup = 3,
FixedUpdate = 4,
PostFixedUpdate = 5,
Update = 6,
PostUpdate = 7,
LateUpdate = 8,
PostLateUpdate = 9,
}
static class PlayerLoopHelper
{
static readonly PlayerLoopRunner[] Runners = new PlayerLoopRunner[10];
static long initialized;
// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void EnsureInitialized()
{
if (Interlocked.CompareExchange(ref initialized, 1, 0) != 0)
return;
for (var i = 0; i < Runners.Length; i++)
{
Runners[i] = new PlayerLoopRunner();
}
var playerLoop =
#if UNITY_2019_3_OR_NEWER
PlayerLoop.GetCurrentPlayerLoop();
#else
PlayerLoop.GetDefaultPlayerLoop();
#endif
var copyList = playerLoop.subSystemList;
ref var initializeSystem = ref FindSubSystem(typeof(Initialization), copyList);
InsertSubsystem(
ref initializeSystem,
null,
new PlayerLoopSystem
{
type = typeof(VContainerInitialization),
updateDelegate = Runners[(int)PlayerLoopTiming.Initialization].Run
},
new PlayerLoopSystem
{
type = typeof(VContainerPostInitialization),
updateDelegate = Runners[(int)PlayerLoopTiming.PostInitialization].Run
});
ref var earlyUpdateSystem = ref FindSubSystem(typeof(EarlyUpdate), copyList);
InsertSubsystem(
ref earlyUpdateSystem,
typeof(EarlyUpdate.ScriptRunDelayedStartupFrame),
new PlayerLoopSystem
{
type = typeof(VContainerStartup),
updateDelegate = Runners[(int)PlayerLoopTiming.Startup].Run
},
new PlayerLoopSystem
{
type = typeof(VContainerPostStartup),
updateDelegate = Runners[(int)PlayerLoopTiming.PostStartup].Run
});
ref var fixedUpdateSystem = ref FindSubSystem(typeof(FixedUpdate), copyList);
InsertSubsystem(
ref fixedUpdateSystem,
typeof(FixedUpdate.ScriptRunBehaviourFixedUpdate),
new PlayerLoopSystem
{
type = typeof(VContainerFixedUpdate),
updateDelegate = Runners[(int)PlayerLoopTiming.FixedUpdate].Run
},
new PlayerLoopSystem
{
type = typeof(VContainerPostFixedUpdate),
updateDelegate = Runners[(int)PlayerLoopTiming.PostFixedUpdate].Run
});
ref var updateSystem = ref FindSubSystem(typeof(Update), copyList);
InsertSubsystem(
ref updateSystem,
typeof(Update.ScriptRunBehaviourUpdate),
new PlayerLoopSystem
{
type = typeof(VContainerUpdate),
updateDelegate = Runners[(int)PlayerLoopTiming.Update].Run
},
new PlayerLoopSystem
{
type = typeof(VContainerPostUpdate),
updateDelegate = Runners[(int)PlayerLoopTiming.PostUpdate].Run
});
ref var lateUpdateSystem = ref FindSubSystem(typeof(PreLateUpdate), copyList);
InsertSubsystem(
ref lateUpdateSystem,
typeof(PreLateUpdate.ScriptRunBehaviourLateUpdate),
new PlayerLoopSystem
{
type = typeof(VContainerLateUpdate),
updateDelegate = Runners[(int)PlayerLoopTiming.LateUpdate].Run
},
new PlayerLoopSystem
{
type = typeof(VContainerPostLateUpdate),
updateDelegate = Runners[(int)PlayerLoopTiming.PostLateUpdate].Run
});
playerLoop.subSystemList = copyList;
PlayerLoop.SetPlayerLoop(playerLoop);
}
public static void Dispatch(PlayerLoopTiming timing, IPlayerLoopItem item)
{
EnsureInitialized();
Runners[(int)timing].Dispatch(item);
}
static ref PlayerLoopSystem FindSubSystem(Type targetType, PlayerLoopSystem[] systems)
{
for (var i = 0; i < systems.Length; i++)
{
if (systems[i].type == targetType)
return ref systems[i];
}
throw new InvalidOperationException($"{targetType.FullName} not in systems");
}
static void InsertSubsystem(
ref PlayerLoopSystem parentSystem,
Type beforeType,
PlayerLoopSystem newSystem,
PlayerLoopSystem newPostSystem)
{
var source = parentSystem.subSystemList;
var insertIndex = -1;
if (beforeType == null)
{
insertIndex = 0;
}
for (var i = 0; i < source.Length; i++)
{
if (source[i].type == beforeType)
{
insertIndex = i;
}
}
if (insertIndex < 0)
{
throw new ArgumentException($"{beforeType.FullName} not in system {parentSystem} {parentSystem.type.FullName}");
}
var dest = new PlayerLoopSystem[source.Length + 2];
for (var i = 0; i < dest.Length; i++)
{
if (i == insertIndex)
{
dest[i] = newSystem;
}
else if (i == dest.Length - 1)
{
dest[i] = newPostSystem;
}
else if (i < insertIndex)
{
dest[i] = source[i];
}
else
{
dest[i] = source[i - 1];
}
}
parentSystem.subSystemList = dest;
}
}
}