using UnityEngine; using UnityEngine.LowLevel; using System; using System.Collections.Concurrent; using System.Threading; namespace JDWTaskSystem { /// <summary> /// Unity PlayerLoop 통합 스케줄러 (Lock-Free 링버버전) /// </summary> public static class JDWPlayerLoopScheduler { private struct Node { public ITaskCompletionAction Action; public ITaskSource Source; } // 비차단 링버퍼 설정 private const int InitialBufferSize = 256; private const int MaxProcessPerFrame = 1000; private static Node[] buffer = new Node[InitialBufferSize]; private static volatile int head = 0; private static volatile int tail = 0; [RuntimeInitializeOnLoadMethod] private static void Integrate() { // 새로운 PlayerLoop 시스템 정의 PlayerLoopSystem loop = PlayerLoop.GetDefaultPlayerLoop(); PlayerLoopSystem system = new PlayerLoopSystem { type = typeof(JDWPlayerLoopScheduler), updateDelegate = Process }; // Update 시스템 뒤에 커스텀 스케줄러 시스템 삽입 InsertSystem(ref loop, typeof(UnityEngine.PlayerLoop.Update), system); PlayerLoop.SetPlayerLoop(loop); } private static void InsertSystem(ref PlayerLoopSystem root, Type anchor, PlayerLoopSystem custom) { for (int i = 0; i < root.subSystemList.Length; i++) { ref PlayerLoopSystem subsystem = ref root.subSystemList[i]; if (subsystem.type == anchor) { // 기존 Update 시스템의 뒤에 새로운 시스템을 삽입하도록 리스트 복사 및 변경 PlayerLoopSystem[] newList = new PlayerLoopSystem[subsystem.subSystemList.Length + 1]; Array.Copy(subsystem.subSystemList, newList, subsystem.subSystemList.Length); newList[newList.Length - 1] = custom; subsystem.subSystemList = newList; break; } } } /// <summary> /// 스케줄러에 작업 등록 /// </summary> /// <param name="action">스케줄링할 작업</param> /// <param name="source">작업 완료 소스</param> public static void Schedule(ITaskCompletionAction action, ITaskSource source) { int currentTail = Interlocked.Increment(ref tail) - 1; int index = currentTail % buffer.Length; // 버퍼 확장 필요 시 if (currentTail >= buffer.Length) { ResizeBuffer(currentTail + 1); } buffer[index] = new Node { Action = action, Source = source }; } private static void ResizeBuffer(int requiredCapacity) { lock (buffer) { if (requiredCapacity <= buffer.Length) return; int newSize = buffer.Length * 2; while (newSize <= requiredCapacity) { newSize *= 2; } var newBuffer = new Node[newSize]; Array.Copy(buffer, newBuffer, buffer.Length); buffer = newBuffer; } } private static void Process() { int processed = 0; int currentHead = head; int currentTail = tail; while (processed++ < MaxProcessPerFrame && currentHead < currentTail) { int index = currentHead % buffer.Length; Node node = buffer[index]; try { node.Action.Invoke(node.Source); } catch (Exception e) { Debug.LogError(e); } Interlocked.Exchange(ref head, ++currentHead); } } } } | cs |
유니티 Update 루프 끝난직후에 스케쥴러가 실행되게
UnityEngine.PlayerLoop.Update 시스템 다음에 스케쥴러 삽입시킴.
Integrate는 게임 플레이 or 에디터 시작 자동호출하고,
InsertSystem()으로 삽입함.
Typeanchor는 삽입위치인데, 여기서 기준점은 Update뒤로 구현
와 ㄳㄳ 멀티쓰레딩 꺼드럭용으로 감사하게 볼게
어려울게 있나 너가 더 잘할껄
솔직히 UniTask 까볼 생각은 안해봤구 걍 알아서] 구현했겠지 했는데 어느정돈 관리할 수 있는게 좀 지리네
https://gall.dcinside.com/board/view/?id=programming&no=2811003&page=1
일반적으로
구현 방식은 아님 내가 CAS를 못해서