A game about forced loneliness, made by TACStudios
1using System.Collections.Generic;
2using Unity.Collections;
3using UnityEngine.Profiling;
4using UnityEngine.Rendering;
5#if UNITY_EDITOR
6using UnityEditor;
7#endif
8
9namespace UnityEngine.U2D.Animation
10{
11 internal class VertexBuffer
12 {
13 /// <summary>
14 /// Number of buffers currently allocated.
15 /// </summary>
16 public int bufferCount => m_Buffers.Length;
17
18 private readonly int m_Id;
19 private bool m_IsActive = true;
20 private int m_DeactivateFrame = -1;
21
22 private NativeByteArray[] m_Buffers;
23 private int m_ActiveIndex = 0;
24
25 public VertexBuffer(int id, int size, bool needDoubleBuffering)
26 {
27 m_Id = id;
28
29 var noOfBuffers = needDoubleBuffering ? 2 : 1;
30 m_Buffers = new NativeByteArray[noOfBuffers];
31 for (var i = 0; i < noOfBuffers; i++)
32 m_Buffers[i] = new NativeByteArray(new NativeArray<byte>(size, Allocator.Persistent, NativeArrayOptions.UninitializedMemory));
33 }
34
35 public override int GetHashCode() => m_Id;
36 private static int GetCurrentFrame() => Time.frameCount;
37
38 public NativeByteArray GetBuffer(int size)
39 {
40 if (!m_IsActive)
41 {
42 Debug.LogError($"Cannot request deactivated buffer. ID: {m_Id}");
43 return null;
44 }
45
46 m_ActiveIndex = (m_ActiveIndex + 1) % m_Buffers.Length;
47 if (m_Buffers[m_ActiveIndex].Length != size)
48 ResizeBuffer(m_ActiveIndex, size);
49
50 return m_Buffers[m_ActiveIndex];
51 }
52
53 private void ResizeBuffer(int bufferId, int newSize)
54 {
55 m_Buffers[bufferId].Dispose();
56 m_Buffers[bufferId] = new NativeByteArray(new NativeArray<byte>(newSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory));
57 }
58
59 public void Deactivate()
60 {
61 if (!m_IsActive)
62 return;
63
64 m_IsActive = false;
65 m_DeactivateFrame = GetCurrentFrame();
66 }
67
68 public void Dispose()
69 {
70 for (var i = 0; i < m_Buffers.Length; i++)
71 {
72 if (m_Buffers[i].IsCreated)
73 m_Buffers[i].Dispose();
74 }
75 }
76
77 public bool IsSafeToDispose() => !m_IsActive && GetCurrentFrame() > m_DeactivateFrame;
78 }
79
80 internal class BufferManager : ScriptableObject
81 {
82 private static BufferManager s_Instance;
83
84 private Dictionary<int, VertexBuffer> m_Buffers = new Dictionary<int, VertexBuffer>();
85 private Queue<VertexBuffer> m_BuffersToDispose = new Queue<VertexBuffer>();
86
87 /// <summary>
88 /// Number of buffers currently allocated.
89 /// </summary>
90 public int bufferCount
91 {
92 get
93 {
94 var count = 0;
95 foreach (var buffer in m_Buffers.Values)
96 count += buffer.bufferCount;
97 return count;
98 }
99 }
100
101 /// <summary>
102 /// Creates two buffers instead of one if enabled.
103 /// </summary>
104 public bool needDoubleBuffering { get; set; }
105
106 public static BufferManager instance
107 {
108 get
109 {
110 if (s_Instance == null)
111 {
112 var bufferMGRs = Resources.FindObjectsOfTypeAll<BufferManager>();
113 if (bufferMGRs.Length > 0)
114 s_Instance = bufferMGRs[0];
115 else
116 s_Instance = ScriptableObject.CreateInstance<BufferManager>();
117 s_Instance.hideFlags = HideFlags.HideAndDontSave;
118 }
119
120 return s_Instance;
121 }
122 }
123
124 private void OnEnable()
125 {
126 if (s_Instance == null)
127 s_Instance = this;
128
129 needDoubleBuffering = SystemInfo.renderingThreadingMode != RenderingThreadingMode.Direct;
130#if UNITY_EDITOR
131 EditorApplication.update += Update;
132#else
133 Application.onBeforeRender += Update;
134#endif
135 }
136
137 private void OnDisable()
138 {
139 if (s_Instance == this)
140 s_Instance = null;
141
142 ForceClearBuffers();
143
144#if UNITY_EDITOR
145 EditorApplication.update -= Update;
146#else
147 Application.onBeforeRender -= Update;
148#endif
149 }
150
151 private void ForceClearBuffers()
152 {
153 foreach (var vertexBuffer in m_Buffers.Values)
154 vertexBuffer.Dispose();
155 foreach (var vertexBuffer in m_BuffersToDispose)
156 vertexBuffer.Dispose();
157
158 m_Buffers.Clear();
159 m_BuffersToDispose.Clear();
160 }
161
162 public NativeByteArray GetBuffer(int id, int bufferSize)
163 {
164 Profiler.BeginSample("BufferManager.GetBuffer");
165 var foundBuffer = m_Buffers.TryGetValue(id, out var buffer);
166 if (!foundBuffer)
167 buffer = CreateBuffer(id, bufferSize);
168
169 Profiler.EndSample();
170 return buffer?.GetBuffer(bufferSize);
171 }
172
173 private VertexBuffer CreateBuffer(int id, int bufferSize)
174 {
175 if (bufferSize < 1)
176 {
177 Debug.LogError("Cannot create a buffer smaller than 1 byte.");
178 return null;
179 }
180
181 var buffer = new VertexBuffer(id, bufferSize, needDoubleBuffering);
182 m_Buffers.Add(id, buffer);
183
184 return buffer;
185 }
186
187 public void ReturnBuffer(int id)
188 {
189 Profiler.BeginSample("BufferManager.ReturnBuffer");
190 if (m_Buffers.TryGetValue(id, out var buffer))
191 {
192 buffer.Deactivate();
193 m_BuffersToDispose.Enqueue(buffer);
194 m_Buffers.Remove(id);
195 }
196
197 Profiler.EndSample();
198 }
199
200 private void Update()
201 {
202 Profiler.BeginSample("BufferManager.Update");
203
204 while (m_BuffersToDispose.Count > 0 && m_BuffersToDispose.Peek().IsSafeToDispose())
205 {
206 var buffer = m_BuffersToDispose.Dequeue();
207 buffer.Dispose();
208 }
209
210 Profiler.EndSample();
211 }
212 }
213}