101 lines
3.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.Rendering.HighDefinition
{
/// <summary>
/// Persistent camera cache for a specific key.
///
/// Use this to have access to a stable HDCamera over frame. Usually you need this when the
/// history buffers of the HDCamera are used.
/// </summary>
/// <typeparam name="K">The type of the key.</typeparam>
class CameraCache<K> : IDisposable
{
Dictionary<K, (Camera camera, int lastFrame)> m_Cache = new Dictionary<K, (Camera camera, int lastFrame)>();
K[] cameraKeysCache = new K[0];
/// <summary> Get or create a camera for the specified key </summary>
/// <param name="key">The key to look at.</param>
/// <param name="frameCount">
/// The current frame count.
///
/// This frame count is assigned to the returned camera to know the age of its last use.
/// </param>
/// <param name="cameraType"> The type of camera to create if one does not exists.</param>
/// <returns>
/// The cached camera if the key was found,
/// otherwise a new camera that was inserted in the cache during the call.
/// </returns>
public Camera GetOrCreate(K key, int frameCount, CameraType cameraType = CameraType.Game)
{
if (m_Cache == null)
throw new ObjectDisposedException(nameof(CameraCache<K>));
if (!m_Cache.TryGetValue(key, out var camera) || camera.camera == null || camera.camera.Equals(null))
{
camera = (new GameObject().AddComponent<Camera>(), frameCount);
camera.camera.cameraType = cameraType;
m_Cache[key] = camera;
}
else
{
camera.lastFrame = frameCount;
m_Cache[key] = camera;
}
return camera.camera;
}
/// <summary> Destroy all cameras that are unused more than <paramref name="frameWindow"/>. </summary>
/// <param name="frameWindow">The age of the cameras to keep.</param>
/// <param name="frameCount">The current frame count. Usually <see cref="Time.frameCount"/>.</param>
public void ClearCamerasUnusedFor(int frameWindow, int frameCount)
{
if (m_Cache == null)
throw new ObjectDisposedException(nameof(CameraCache<K>));
// In case cameraKeysCache length does not matches the current cache length, we resize it:
if (cameraKeysCache.Length != m_Cache.Count)
cameraKeysCache = new K[m_Cache.Count];
// Copy keys to remove them from the dictionary (avoids collection modifed while iterating error)
m_Cache.Keys.CopyTo(cameraKeysCache, 0);
foreach (var key in cameraKeysCache)
{
if (m_Cache.TryGetValue(key, out var value))
{
if (Math.Abs(frameCount - value.lastFrame) > frameWindow)
{
if (value.camera != null)
{
CoreUtils.Destroy(value.camera.gameObject);
}
m_Cache.Remove(key);
}
}
}
}
/// <summary>Destroy all cameras in the cache.</summary>
public void Clear()
{
if (m_Cache == null)
throw new ObjectDisposedException(nameof(CameraCache<K>));
foreach (var pair in m_Cache)
{
if (pair.Value.camera != null)
CoreUtils.Destroy(pair.Value.camera.gameObject);
}
m_Cache.Clear();
}
public void Dispose()
{
Clear();
m_Cache = null;
}
}
}