internal static void Sort <T>(List <T> items, GetSystemType <T> getType, Type parentType) { #if !NET_DOTS lookupDictionary = null; #endif // Populate dictionary mapping systemType to system-and-before/after-types. // This is clunky - it is easier to understand, and cleaner code, to // just use a Dictionary<Type, SysAndDep>. However, Tiny doesn't currently have // the ability to use Type as a key to a NativeHash, so we're stuck until that gets addressed. // // Likewise, this is important shared code. It can be done cleaner with 2 versions, but then... // 2 sets of bugs and slightly different behavior will creep in. // var sysAndDep = new SysAndDep <T> [items.Count]; for (int i = 0; i < items.Count; ++i) { var sys = items[i]; sysAndDep[i] = new SysAndDep <T> { item = sys, type = getType(sys), updateBefore = new List <Type>(), nAfter = 0, }; } for (int i = 0; i < sysAndDep.Length; ++i) { var systemType = sysAndDep[i].type; var before = TypeManager.GetSystemAttributes(systemType, typeof(UpdateBeforeAttribute)); var after = TypeManager.GetSystemAttributes(systemType, typeof(UpdateAfterAttribute)); foreach (var attr in before) { var dep = attr as UpdateBeforeAttribute; if (!typeof(ComponentSystemBase).IsAssignableFrom(dep.SystemType)) { #if !NET_DOTS Debug.LogWarning( $"Ignoring invalid [UpdateBefore] attribute on {systemType} because {dep.SystemType} is not a subclass of {nameof(ComponentSystemBase)}.\n" + $"Set the target parameter of [UpdateBefore] to a system class in the same {nameof(ComponentSystemGroup)} as {systemType}."); #else Debug.LogWarning($"WARNING: invalid [UpdateBefore] attribute:"); Debug.LogWarning(TypeManager.GetSystemName(dep.SystemType)); Debug.LogWarning(" is not derived from ComponentSystemBase. Set the target parameter of [UpdateBefore] to a system class in the same ComponentSystemGroup."); #endif continue; } if (dep.SystemType == systemType) { #if !NET_DOTS Debug.LogWarning( $"Ignoring invalid [UpdateBefore] attribute on {systemType} because a system cannot be updated before itself.\n" + $"Set the target parameter of [UpdateBefore] to a different system class in the same {nameof(ComponentSystemGroup)} as {systemType}."); #else Debug.LogWarning($"WARNING: invalid [UpdateBefore] attribute:"); Debug.LogWarning(TypeManager.GetSystemName(systemType)); Debug.LogWarning(" depends on itself. Set the target parameter of [UpdateBefore] to a system class in the same ComponentSystemGroup."); #endif continue; } if (parentType == typeof(InitializationSystemGroup)) { if (dep.SystemType == typeof(BeginInitializationEntityCommandBufferSystem)) { #if !NET_DOTS throw new ArgumentException( $"Invalid [UpdateBefore] {dep.SystemType} attribute on {systemType}, because that system is already restricted to be first."); #else throw new ArgumentException($"Invalid [UpdateBefore] BeginInitializationEntityCommandBufferSystem, because that system is already restricted to be first."); #endif } if (dep.SystemType == typeof(EndInitializationEntityCommandBufferSystem)) { WarningForBeforeCheck(systemType, dep.SystemType); continue; } } if (parentType == typeof(SimulationSystemGroup)) { if (dep.SystemType == typeof(BeginSimulationEntityCommandBufferSystem)) { #if !NET_DOTS throw new ArgumentException( $"Invalid [UpdateBefore] {dep.SystemType} attribute on {systemType}, because that system is already restricted to be first."); #else throw new ArgumentException($"Invalid [UpdateBefore] BeginSimulationEntityCommandBufferSystem, because that system is already restricted to be first."); #endif } if (dep.SystemType == typeof(LateSimulationSystemGroup)) { WarningForBeforeCheck(systemType, dep.SystemType); continue; } if (dep.SystemType == typeof(EndSimulationEntityCommandBufferSystem)) { WarningForBeforeCheck(systemType, dep.SystemType); continue; } } if (parentType == typeof(PresentationSystemGroup)) { if (dep.SystemType == typeof(BeginPresentationEntityCommandBufferSystem)) { #if !NET_DOTS throw new ArgumentException( $"Invalid [UpdateBefore] {dep.SystemType} attribute on {systemType}, because that system is already restricted to be first."); #else throw new ArgumentException($"Invalid [UpdateBefore] BeginPreesntationEntityCommandBufferSystem, because that system is already restricted to be first."); #endif } } int depIndex = LookupSysAndDep(dep.SystemType, sysAndDep); if (depIndex < 0) { #if !NET_DOTS Debug.LogWarning( $"Ignoring invalid [UpdateBefore] attribute on {systemType} because {dep.SystemType} belongs to a different {nameof(ComponentSystemGroup)}.\n" + $"This attribute can only order systems that are children of the same {nameof(ComponentSystemGroup)}.\n" + $"Make sure that both systems are in the same parent group with [UpdateInGroup(typeof({parentType})].\n" + $"You can also change the relative order of groups when appropriate, by using [UpdateBefore] and [UpdateAfter] attributes at the group level."); #else Debug.LogWarning("WARNING: invalid [UpdateBefore] dependency:"); Debug.LogWarning(TypeManager.GetSystemName(systemType)); Debug.LogWarning(" depends on a non-sibling system: "); Debug.LogWarning(TypeManager.GetSystemName(dep.SystemType)); #endif continue; } sysAndDep[i].updateBefore.Add(dep.SystemType); sysAndDep[depIndex].nAfter++; } foreach (var attr in after) { var dep = attr as UpdateAfterAttribute; if (!typeof(ComponentSystemBase).IsAssignableFrom(dep.SystemType)) { #if !NET_DOTS Debug.LogWarning( $"Ignoring invalid [UpdateAfter] attribute on {systemType} because {dep.SystemType} is not a subclass of {nameof(ComponentSystemBase)}.\n" + $"Set the target parameter of [UpdateAfter] to a system class in the same {nameof(ComponentSystemGroup)} as {systemType}."); #else Debug.LogWarning($"WARNING: invalid [UpdateAfter] attribute:"); Debug.LogWarning(TypeManager.GetSystemName(dep.SystemType)); Debug.LogWarning(" is not derived from ComponentSystemBase. Set the target parameter of [UpdateAfter] to a system class in the same ComponentSystemGroup."); #endif continue; } if (dep.SystemType == systemType) { #if !NET_DOTS Debug.LogWarning( $"Ignoring invalid [UpdateAfter] attribute on {systemType} because a system cannot be updated after itself.\n" + $"Set the target parameter of [UpdateAfter] to a different system class in the same {nameof(ComponentSystemGroup)} as {systemType}."); #else Debug.LogWarning($"WARNING: invalid [UpdateAfter] attribute:"); Debug.LogWarning(TypeManager.GetSystemName(systemType)); Debug.LogWarning(" depends on itself. Set the target parameter of [UpdateAfter] to a system class in the same ComponentSystemGroup."); #endif continue; } if (parentType == typeof(InitializationSystemGroup)) { if (dep.SystemType == typeof(BeginInitializationEntityCommandBufferSystem)) { WarningForAfterCheck(systemType, dep.SystemType); continue; } if (dep.SystemType == typeof(EndInitializationEntityCommandBufferSystem)) { #if !NET_DOTS throw new ArgumentException( $"Invalid [UpdateAfter] {dep.SystemType} attribute on {systemType}, because that system is already restricted to be last."); #else throw new ArgumentException($"Invalid [UpdateAfter] EndInitializationEntityCommandBufferSystem, because that system is already restricted to be last."); #endif } } if (parentType == typeof(SimulationSystemGroup)) { if (dep.SystemType == typeof(BeginSimulationEntityCommandBufferSystem)) { WarningForAfterCheck(systemType, dep.SystemType); continue; } if (dep.SystemType == typeof(LateSimulationSystemGroup)) { #if !NET_DOTS throw new ArgumentException( $"Invalid [UpdateAfter] {dep.SystemType} attribute on {systemType}, because that system is already restricted to be last."); #else throw new ArgumentException($"Invalid [UpdateAfter] EndLateSimulationEntityCommandBufferSystem, because that system is already restricted to be last."); #endif } if (dep.SystemType == typeof(EndSimulationEntityCommandBufferSystem)) { #if !NET_DOTS throw new ArgumentException( $"Invalid [UpdateAfter] {dep.SystemType} attribute on {systemType}, because that system is already restricted to be last."); #else throw new ArgumentException($"Invalid [UpdateAfter] EndSimulationEntityCommandBufferSystem, because that system is already restricted to be last."); #endif } } if (parentType == typeof(PresentationSystemGroup)) { if (dep.SystemType == typeof(BeginPresentationEntityCommandBufferSystem)) { WarningForAfterCheck(systemType, dep.SystemType); continue; } } int depIndex = LookupSysAndDep(dep.SystemType, sysAndDep); if (depIndex < 0) { #if !NET_DOTS Debug.LogWarning( $"Ignoring invalid [UpdateAfter] attribute on {systemType} because {dep.SystemType} belongs to a different {nameof(ComponentSystemGroup)}.\n" + $"This attribute can only order systems that are children of the same {nameof(ComponentSystemGroup)}.\n" + $"Make sure that both systems are in the same parent group with [UpdateInGroup(typeof({parentType})].\n" + $"You can also change the relative order of groups when appropriate, by using [UpdateBefore] and [UpdateAfter] attributes at the group level."); #else Debug.LogWarning("WARNING: invalid [UpdateAfter] dependency:"); Debug.LogWarning(TypeManager.GetSystemName(systemType)); Debug.LogWarning(" depends on a non-sibling system: "); Debug.LogWarning(TypeManager.GetSystemName(dep.SystemType)); #endif continue; } sysAndDep[depIndex].updateBefore.Add(systemType); sysAndDep[i].nAfter++; } } // Clear the systems list and rebuild it in sorted order from the lookup table var readySystems = new Heap <TypeHeapElement>(items.Count); items.Clear(); for (int i = 0; i < sysAndDep.Length; ++i) { if (sysAndDep[i].nAfter == 0) { readySystems.Insert(new TypeHeapElement(i, sysAndDep[i].type)); } } while (!readySystems.Empty) { var sysIndex = readySystems.Extract().unsortedIndex; var sd = sysAndDep[sysIndex]; sysAndDep[sysIndex] = new SysAndDep <T>(); // "Remove()" items.Add(sd.item); foreach (var beforeType in sd.updateBefore) { int beforeIndex = LookupSysAndDep(beforeType, sysAndDep); if (beforeIndex < 0) { throw new Exception("Bug in SortSystemUpdateList(), beforeIndex < 0"); } if (sysAndDep[beforeIndex].nAfter <= 0) { throw new Exception("Bug in SortSystemUpdateList(), nAfter <= 0"); } sysAndDep[beforeIndex].nAfter--; if (sysAndDep[beforeIndex].nAfter == 0) { readySystems.Insert(new TypeHeapElement(beforeIndex, sysAndDep[beforeIndex].type)); } } } for (int i = 0; i < sysAndDep.Length; ++i) { if (sysAndDep[i].item != null) { // Since no System in the circular dependency would have ever been added // to the heap, we should have values for everything in sysAndDep. Check, // just in case. #if ENABLE_UNITY_COLLECTIONS_CHECKS var visitedSystems = new List <Type>(); var startIndex = i; var currentIndex = i; while (true) { if (sysAndDep[currentIndex].item != null) { visitedSystems.Add(sysAndDep[currentIndex].type); } currentIndex = LookupSysAndDep(sysAndDep[currentIndex].updateBefore[0], sysAndDep); if (currentIndex < 0 || currentIndex == startIndex || sysAndDep[currentIndex].item == null) { throw new CircularSystemDependencyException(visitedSystems); } } #else sysAndDep[i] = new SysAndDep <T>(); #endif } } }
public virtual void SortSystemUpdateList() { if (!m_systemSortDirty) { return; } m_systemSortDirty = false; #if !NET_DOTS lookupDictionary = null; #endif // Populate dictionary mapping systemType to system-and-before/after-types. // This is clunky - it is easier to understand, and cleaner code, to // just use a Dictionary<Type, SysAndDep>. However, Tiny doesn't currently have // the ability to use Type as a key to a NativeHash, so we're stuck until that gets addressed. // // Likewise, this is important shared code. It can be done cleaner with 2 versions, but then... // 2 sets of bugs and slightly different behavior will creep in. // var sysAndDep = new SysAndDep[m_systemsToUpdate.Count]; for (int i = 0; i < m_systemsToUpdate.Count; ++i) { var sys = m_systemsToUpdate[i]; if (TypeManager.IsSystemAGroup(sys.GetType())) { (sys as ComponentSystemGroup).SortSystemUpdateList(); } sysAndDep[i] = new SysAndDep { system = sys, updateBefore = new List <Type>(), nAfter = 0, }; } for (int i = 0; i < m_systemsToUpdate.Count; ++i) { var sys = m_systemsToUpdate[i]; var before = TypeManager.GetSystemAttributes(sys.GetType(), typeof(UpdateBeforeAttribute)); var after = TypeManager.GetSystemAttributes(sys.GetType(), typeof(UpdateAfterAttribute)); foreach (var attr in before) { var dep = attr as UpdateBeforeAttribute; int depIndex = LookupSysAndDep(dep.SystemType, sysAndDep); if (depIndex < 0) { #if !NET_DOTS Debug.LogWarning("Ignoring invalid [UpdateBefore] dependency for " + sys.GetType() + ": " + dep.SystemType + " must be a member of the same ComponentSystemGroup."); #else Debug.LogWarning("WARNING: invalid [UpdateBefore] dependency:"); Debug.LogWarning(TypeManager.SystemName(sys.GetType())); Debug.LogWarning(" depends on a non-sibling or non-ComponentSystem."); #endif continue; } sysAndDep[i].updateBefore.Add(dep.SystemType); sysAndDep[depIndex].nAfter++; } foreach (var attr in after) { var dep = attr as UpdateAfterAttribute; int depIndex = LookupSysAndDep(dep.SystemType, sysAndDep); if (depIndex < 0) { #if !NET_DOTS Debug.LogWarning("Ignoring invalid [UpdateAfter] dependency for " + sys.GetType() + ": " + dep.SystemType + " must be a member of the same ComponentSystemGroup."); #else Debug.LogWarning("WARNING: invalid [UpdateAfter] dependency:"); Debug.LogWarning(TypeManager.SystemName(sys.GetType())); Debug.LogWarning(" depends on a non-sibling or non-ComponentSystem."); #endif continue; } sysAndDep[depIndex].updateBefore.Add(sys.GetType()); sysAndDep[i].nAfter++; } } // Clear the systems list and rebuild it in sorted order from the lookup table var readySystems = new Heap <TypeHeapElement>(m_systemsToUpdate.Count); m_systemsToUpdate.Clear(); for (int i = 0; i < sysAndDep.Length; ++i) { if (sysAndDep[i].nAfter == 0) { readySystems.Insert(new TypeHeapElement(i, sysAndDep[i].system.GetType())); } } while (!readySystems.Empty) { int sysIndex = readySystems.Extract().unsortedIndex; SysAndDep sd = sysAndDep[sysIndex]; Type sysType = sd.system.GetType(); sysAndDep[sysIndex] = new SysAndDep(); // "Remove()" m_systemsToUpdate.Add(sd.system); foreach (var beforeType in sd.updateBefore) { int beforeIndex = LookupSysAndDep(beforeType, sysAndDep); if (beforeIndex < 0) { throw new Exception("Bug in SortSystemUpdateList(), beforeIndex < 0"); } if (sysAndDep[beforeIndex].nAfter <= 0) { throw new Exception("Bug in SortSystemUpdateList(), nAfter <= 0"); } sysAndDep[beforeIndex].nAfter--; if (sysAndDep[beforeIndex].nAfter == 0) { readySystems.Insert(new TypeHeapElement(beforeIndex, sysAndDep[beforeIndex].system.GetType())); } } } for (int i = 0; i < sysAndDep.Length; ++i) { if (sysAndDep[i].system != null) { // Since no System in the circular dependency would have ever been added // to the heap, we should have values for everything in sysAndDep. Check, // just in case. #if ENABLE_UNITY_COLLECTIONS_CHECKS var visitedSystems = new List <ComponentSystemBase>(); var startIndex = i; var currentIndex = i; while (true) { if (sysAndDep[currentIndex].system != null) { visitedSystems.Add(sysAndDep[currentIndex].system); } currentIndex = LookupSysAndDep(sysAndDep[currentIndex].updateBefore[0], sysAndDep); if (currentIndex < 0 || currentIndex == startIndex || sysAndDep[currentIndex].system == null) { throw new CircularSystemDependencyException(visitedSystems); } } #else sysAndDep[i] = new SysAndDep(); #endif } } }
internal static void Sort(List <SystemElement> elements, Type parentType) { #if !NET_DOTS lookupDictionary = null; #endif // Populate dictionary mapping systemType to system-and-before/after-types. // This is clunky - it is easier to understand, and cleaner code, to // just use a Dictionary<Type, SysAndDep>. However, Tiny doesn't currently have // the ability to use Type as a key to a NativeHash, so we're stuck until that gets addressed. // // Likewise, this is important shared code. It can be done cleaner with 2 versions, but then... // 2 sets of bugs and slightly different behavior will creep in. // var sysAndDep = new SysAndDep[elements.Count]; for (int i = 0; i < elements.Count; ++i) { var elem = elements[i]; sysAndDep[i] = new SysAndDep { type = elem.Type, externalIndex = elem.Index, updateBefore = new List <Type>(), nAfter = 0, }; } FindConstraints(parentType, sysAndDep); // Clear the systems list and rebuild it in sorted order from the lookup table elements.Clear(); var readySystems = new Heap(sysAndDep.Length); for (int i = 0; i < sysAndDep.Length; ++i) { if (sysAndDep[i].nAfter == 0) { readySystems.Insert(new TypeHeapElement(i, sysAndDep[i].type)); } } while (!readySystems.Empty) { var sysIndex = readySystems.Extract().unsortedIndex; var sd = sysAndDep[sysIndex]; sysAndDep[sysIndex] = default; // "Remove()" elements.Add(new SystemElement { Type = sd.type, Index = sd.externalIndex }); foreach (var beforeType in sd.updateBefore) { int beforeIndex = LookupSysAndDep(beforeType, sysAndDep); if (beforeIndex < 0) { throw new Exception("Bug in SortSystemUpdateList(), beforeIndex < 0"); } if (sysAndDep[beforeIndex].nAfter <= 0) { throw new Exception("Bug in SortSystemUpdateList(), nAfter <= 0"); } sysAndDep[beforeIndex].nAfter--; if (sysAndDep[beforeIndex].nAfter == 0) { readySystems.Insert(new TypeHeapElement(beforeIndex, sysAndDep[beforeIndex].type)); } } } for (int i = 0; i < sysAndDep.Length; ++i) { if (sysAndDep[i].type != null) { // Since no System in the circular dependency would have ever been added // to the heap, we should have values for everything in sysAndDep. Check, // just in case. #if ENABLE_UNITY_COLLECTIONS_CHECKS var visitedSystems = new List <Type>(); var startIndex = i; var currentIndex = i; while (true) { if (sysAndDep[currentIndex].type != null) { visitedSystems.Add(sysAndDep[currentIndex].type); } currentIndex = LookupSysAndDep(sysAndDep[currentIndex].updateBefore[0], sysAndDep); if (currentIndex < 0 || currentIndex == startIndex || sysAndDep[currentIndex].type == null) { throw new CircularSystemDependencyException(visitedSystems); } } #else sysAndDep[i] = default; #endif } } }