protected override void OnUpdate() { #if UNITY_EDITOR // Live edit if (EditorApplication.isPlaying && m_Version != LastVersion) { m_Version = LastVersion; foreach (var contextsValue in m_Contexts.Values) { contextsValue?.Dispose(); } m_Contexts.Clear(); EntityManager.RemoveComponent <ScriptingGraphInstance>(m_Query); } #endif Entities.With(m_UninitializedQuery).ForEach((Entity e, ScriptingGraph g) => { // Start var inputs = EntityManager.HasComponent <ValueInput>(e) ? EntityManager.GetBuffer <ValueInput>(e) : new DynamicBuffer <ValueInput>(); GraphInstance ctx = CreateEntityContext(inputs, e, g.ScriptingGraphAsset.Definition); #if VS_TRACING ctx.ScriptingGraphAssetID = g.ScriptingGraphAsset.GetInstanceID(); #endif EntityManager.AddComponentData(e, new ScriptingGraphInstance()); EntityManager.AddComponentData(e, new ScriptingGraphInstanceAlive()); #if !UNITY_EDITOR // keep it for live edit EntityManager.RemoveComponent <ScriptingGraph>(e); #endif if (!m_ShouldTriggerEventJob) { m_ShouldTriggerEventJob = ctx.ContainsEventReceiver; } m_EventFieldDescriptions = ctx.EventFields; }); #if VS_DOTS_PHYSICS_EXISTS VisualScriptingPhysics.SetupCollisionTriggerData(EntityManager, FrameCount, ref m_TriggerData, m_Query, VisualScriptingPhysics.EventType.Trigger); VisualScriptingPhysics.SetupCollisionTriggerData(EntityManager, FrameCount, ref m_CollisionData, m_Query, VisualScriptingPhysics.EventType.Collision); #endif // A list: I assume the most common case is "the entity has not been destroyed" NativeList <Entity> destroyed = new NativeList <Entity>(Allocator.Temp); Entities.With(m_Query).ForEach((Entity e) => { var beingDestroyed = m_BeingDestroyedQueryMask.Matches(e); if (!m_Contexts.TryGetValue(e, out var ctx)) { return; } // used for random seed ctx.LastSystemVersion = LastSystemVersion; ctx.ResetFrame(); #if VS_TRACING if (s_TracingEnabled && ctx.FrameTrace == null) { ctx.FrameTrace = new DotsFrameTrace(Allocator.Persistent); } #endif #if VS_DOTS_PHYSICS_EXISTS TriggerPhysicsEvents(e, ref m_TriggerData, VisualScriptingPhysics.TriggerEventId); TriggerPhysicsEvents(e, ref m_CollisionData, VisualScriptingPhysics.CollisionEventId); #endif }); // Retrieve events that are dispatched from code if (m_ShouldTriggerEventJob) { m_DispatchedEvents.AddRange( VisualScriptingEventUtility.GetEventsFromApi(m_EventSystem, m_EventFieldDescriptions)); } // run each graph while there's either: // - a graph reference whose output trigger has been activated // - a graph input trigger has been activated by another graph referencing it // - events dispatched var ocount = m_OutputTriggersPerEntityGraphActivated.Count(); var icount = m_InputTriggersPerEntityGraphActivated.Count(); int iteration = 0; bool secondOutputTriggersMap = true; var dispatchedEvents = new List <EventNodeData>(); while (iteration == 0 || iteration < 100 && (ocount > 0 || icount > 0 || m_DispatchedEvents.Count > 0)) { // LogIterationReason(); dispatchedEvents.Clear(); ocount = 0; Entities.With(m_Query).ForEach(e => { if (!m_Contexts.TryGetValue(e, out GraphInstance ctx)) { return; } var beingDestroyed = m_BeingDestroyedQueryMask.Matches(e); if (beingDestroyed) { destroyed.Add(e); } // swap two hash maps - one for the current iteration and one for the next one, clear and swap after each iteration. // for some reason var tmp = h1; h1 = h2; h2 = tmp; didn't work var events = ctx.GlobalToLocalEventData(m_DispatchedEvents).ToList(); for (var i = 0; i < events.Count; ++i) { bool eventsTriggered = false; var evt = events[i]; // TODO: move that out of the loop once we remove the evt parameter of ResumeFrame and process events/inputs/graphrefs right at once if (eventsTriggered) { // keep filling the same output map. events might trigger graph outputs ctx.ResumeFrame(e, Time, evt, secondOutputTriggersMap ? m_OutputTriggersPerEntityGraphActivated : m_OutputTriggersPerEntityGraphActivated2); } } // this call will remove used entries from the input map var resumeInputs = ctx.TriggerGraphInputs(e, m_InputTriggersPerEntityGraphActivated); // this one won't as we need to process them for each graph. use the output map previously filled by the first ResumeFrame call and maybe the subsequent ones in the event loop var triggerGraphReferences = ctx.TriggerGraphReferences(e, secondOutputTriggersMap ? m_OutputTriggersPerEntityGraphActivated : m_OutputTriggersPerEntityGraphActivated2); if (iteration == 0 || resumeInputs || triggerGraphReferences) { // fill the other output map. ctx.ResumeFrame(e, Time, default, secondOutputTriggersMap
protected override void OnUpdate() { #if UNITY_EDITOR // Live edit if (m_Version != LastVersion) { m_Version = LastVersion; foreach (var contextsValue in m_Contexts.Values) { contextsValue?.Dispose(); } m_Contexts.Clear(); EntityManager.RemoveComponent <ScriptingGraphInstance>(m_Query); } #endif Entities.With(m_UninitializedQuery).ForEach((Entity e, ScriptingGraph g) => { // Start var inputs = EntityManager.HasComponent <ValueInput>(e) ? EntityManager.GetBuffer <ValueInput>(e) : new DynamicBuffer <ValueInput>(); GraphInstance ctx = CreateEntityContext(inputs, e, g.ScriptingGraphAsset.Definition); #if VS_TRACING ctx.ScriptingGraphAssetID = g.ScriptingGraphAsset.GetInstanceID(); #endif EntityManager.AddComponentData(e, new ScriptingGraphInstance()); EntityManager.AddComponentData(e, new ScriptingGraphInstanceAlive()); #if !UNITY_EDITOR // keep it for live edit EntityManager.RemoveComponent <ScriptingGraph>(e); #endif }); #if VS_DOTS_PHYSICS_EXISTS VisualScriptingPhysics.SetupCollisionTriggerData(EntityManager, ref m_TriggerData, m_Query, VisualScriptingPhysics.EventType.Trigger); VisualScriptingPhysics.SetupCollisionTriggerData(EntityManager, ref m_CollisionData, m_Query, VisualScriptingPhysics.EventType.Collision); #endif // A list: I assume the most common case is "the entity has not been destroyed" NativeList <Entity> destroyed = new NativeList <Entity>(Allocator.Temp); Entities.With(m_Query).ForEach((Entity e) => { var beingDestroyed = m_BeingDestroyedQueryMask.Matches(e); GraphInstance ctx; //Get the context if (beingDestroyed) { if (!m_Contexts.TryGetValue(e, out ctx)) { return; } destroyed.Add(e); } else { if (!m_Contexts.TryGetValue(e, out ctx)) { // MBRIAU: Should this be an error? return; } } ctx.LastSystemVersion = LastSystemVersion; ctx.ResetFrame(); // TODO move at the end #if VS_TRACING if (ctx.FrameTrace == null) { ctx.FrameTrace = new DotsFrameTrace(Allocator.Persistent); } #endif if (beingDestroyed) { ctx.TriggerEntryPoints <OnDestroy>(); ctx.RunFrame(e, Time); EntityManager.RemoveComponent <ScriptingGraphInstance>(e); EntityManager.RemoveComponent <ScriptingGraph>(e); } else { // Start if (ctx.IsStarting) { ctx.TriggerEntryPoints <OnStart>(); ctx.IsStarting = false; } // Update ctx.TriggerEntryPoints <OnUpdate>(); ctx.TriggerEntryPoints <OnKey>(); #if VS_DOTS_PHYSICS_EXISTS TriggerPhysicsEvents(e, ref m_TriggerData, VisualScriptingPhysics.TriggerEventId); TriggerPhysicsEvents(e, ref m_CollisionData, VisualScriptingPhysics.CollisionEventId); #endif // Actually execute all nodes active ctx.RunFrame(e, Time); m_DispatchedEvents.AddRange(ctx.DispatchedEvents); } }); TriggerEvents(m_DispatchedEvents); #if VS_TRACING foreach (var graphInstancePair in m_Contexts) { var graphInstance = graphInstancePair.Value; if (graphInstance?.FrameTrace != null) { DotsFrameTrace.FlushFrameTrace(graphInstance.ScriptingGraphAssetID, UnityEngine.Time.frameCount, graphInstance.CurrentEntity, #if UNITY_EDITOR EntityManager.GetName(graphInstance.CurrentEntity), #else e.Index.ToString(), #endif graphInstance.FrameTrace); graphInstance.FrameTrace = null; } } #endif for (var index = 0; index < destroyed.Length; index++) { var entity = destroyed[index]; m_Contexts[entity].Dispose(); m_Contexts.Remove(entity); } destroyed.Dispose(); }