using System.Collections.Generic;
using Unity.Collections;
using UnityEngine.Profiling;
using UnityEngine.Rendering;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.U2D.Animation
{
internal class VertexBuffer
{
///
/// Number of buffers currently allocated.
///
public int bufferCount => m_Buffers.Length;
private readonly int m_Id;
private bool m_IsActive = true;
private int m_DeactivateFrame = -1;
private NativeByteArray[] m_Buffers;
private int m_ActiveIndex = 0;
public VertexBuffer(int id, int size, bool needDoubleBuffering)
{
m_Id = id;
var noOfBuffers = needDoubleBuffering ? 2 : 1;
m_Buffers = new NativeByteArray[noOfBuffers];
for (var i = 0; i < noOfBuffers; i++)
m_Buffers[i] = new NativeByteArray(new NativeArray(size, Allocator.Persistent, NativeArrayOptions.UninitializedMemory));
}
public override int GetHashCode() => m_Id;
private static int GetCurrentFrame() => Time.frameCount;
public NativeByteArray GetBuffer(int size)
{
if (!m_IsActive)
{
Debug.LogError($"Cannot request deactivated buffer. ID: {m_Id}");
return null;
}
m_ActiveIndex = (m_ActiveIndex + 1) % m_Buffers.Length;
if (m_Buffers[m_ActiveIndex].Length != size)
ResizeBuffer(m_ActiveIndex, size);
return m_Buffers[m_ActiveIndex];
}
private void ResizeBuffer(int bufferId, int newSize)
{
m_Buffers[bufferId].Dispose();
m_Buffers[bufferId] = new NativeByteArray(new NativeArray(newSize, Allocator.Persistent, NativeArrayOptions.UninitializedMemory));
}
public void Deactivate()
{
if (!m_IsActive)
return;
m_IsActive = false;
m_DeactivateFrame = GetCurrentFrame();
}
public void Dispose()
{
for (var i = 0; i < m_Buffers.Length; i++)
{
if (m_Buffers[i].IsCreated)
m_Buffers[i].Dispose();
}
}
public bool IsSafeToDispose() => !m_IsActive && GetCurrentFrame() > m_DeactivateFrame;
}
internal class BufferManager : ScriptableObject
{
private static BufferManager s_Instance;
private Dictionary m_Buffers = new Dictionary();
private Queue m_BuffersToDispose = new Queue();
///
/// Number of buffers currently allocated.
///
public int bufferCount
{
get
{
var count = 0;
foreach (var buffer in m_Buffers.Values)
count += buffer.bufferCount;
return count;
}
}
///
/// Creates two buffers instead of one if enabled.
///
public bool needDoubleBuffering
{
get;
set;
}
public static BufferManager instance
{
get
{
if (s_Instance == null)
{
var bufferMGRs = Resources.FindObjectsOfTypeAll();
if (bufferMGRs.Length > 0)
s_Instance = bufferMGRs[0];
else
s_Instance = ScriptableObject.CreateInstance();
s_Instance.hideFlags = HideFlags.HideAndDontSave;
}
return s_Instance;
}
}
private void OnEnable()
{
if (s_Instance == null)
s_Instance = this;
needDoubleBuffering = SystemInfo.renderingThreadingMode != RenderingThreadingMode.Direct;
#if UNITY_EDITOR
EditorApplication.update += Update;
#else
Application.onBeforeRender += Update;
#endif
}
private void OnDisable()
{
if (s_Instance == this)
s_Instance = null;
ForceClearBuffers();
#if UNITY_EDITOR
EditorApplication.update -= Update;
#else
Application.onBeforeRender -= Update;
#endif
}
private void ForceClearBuffers()
{
foreach (var vertexBuffer in m_Buffers.Values)
vertexBuffer.Dispose();
foreach (var vertexBuffer in m_BuffersToDispose)
vertexBuffer.Dispose();
m_Buffers.Clear();
m_BuffersToDispose.Clear();
}
public NativeByteArray GetBuffer(int id, int bufferSize)
{
Profiler.BeginSample("BufferManager.GetBuffer");
var foundBuffer = m_Buffers.TryGetValue(id, out var buffer);
if (!foundBuffer)
buffer = CreateBuffer(id, bufferSize);
Profiler.EndSample();
return buffer?.GetBuffer(bufferSize);
}
private VertexBuffer CreateBuffer(int id, int bufferSize)
{
if (bufferSize < 1)
{
Debug.LogError("Cannot create a buffer smaller than 1 byte.");
return null;
}
var buffer = new VertexBuffer(id, bufferSize, needDoubleBuffering);
m_Buffers.Add(id, buffer);
return buffer;
}
public void ReturnBuffer(int id)
{
Profiler.BeginSample("BufferManager.ReturnBuffer");
if (m_Buffers.TryGetValue(id, out var buffer))
{
buffer.Deactivate();
m_BuffersToDispose.Enqueue(buffer);
m_Buffers.Remove(id);
}
Profiler.EndSample();
}
private void Update()
{
Profiler.BeginSample("BufferManager.Update");
while (m_BuffersToDispose.Count > 0 && m_BuffersToDispose.Peek().IsSafeToDispose())
{
var buffer = m_BuffersToDispose.Dequeue();
buffer.Dispose();
}
Profiler.EndSample();
}
}
}