You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

117 lines
4.6 KiB

using System;
using System.Collections.Generic;
namespace UnityEngine.Rendering.HighDefinition
{
// Multi-layered camera cache for reflection probes.
// The goal is to keep a pool of camera GameObjects to avoid reallocating them regularly (especially when doing OnDemand updates)
// On top of that, we keep a map of cameras currently used for a particular probe/face/position tuple. This allows us to keep frame to frame history coherency for real time probes.
class ProbeCameraCache<K> : IDisposable
{
// Pool of cameras
Stack<Camera> m_CameraPool = new Stack<Camera>();
// Map of currently used cameras.
Dictionary<K, (Camera camera, int lastFrame)> m_Cache = new Dictionary<K, (Camera camera, int lastFrame)>();
// Only used as temporary container.
K[] m_TempCameraKeysCache = new K[0];
internal int cachedActiveCameraCount => m_CameraPool.Count;
// If the key exists, we can reuse the camera. It means we are rendering the same probe/face
// If it does not, it means we need a new camera. We either get one from the pool or create a new one.
public Camera GetOrCreate(K key, int frameCount)
{
if (m_Cache == null)
throw new ObjectDisposedException(nameof(ProbeCameraCache<K>));
if (!m_Cache.TryGetValue(key, out var probeCamera) || probeCamera.camera == null || probeCamera.camera.Equals(null))
{
// Key isn't currently used, we try to get an existing or new camera from the pool.
if (m_CameraPool.Count == 0)
{
var cameraGameObject = new GameObject("Unused Probe Camera")
{ hideFlags = HideFlags.HideAndDontSave };
#if !UNITY_EDITOR
GameObject.DontDestroyOnLoad(cameraGameObject);
#endif
probeCamera = (cameraGameObject.AddComponent<Camera>(), frameCount);
probeCamera.camera.cameraType = CameraType.Reflection;
cameraGameObject.SetActive(false);
}
else
probeCamera = (m_CameraPool.Pop(), frameCount);
m_Cache[key] = probeCamera;
}
else
{
// Key already exists. Just update the current frame index.
probeCamera.lastFrame = frameCount;
m_Cache[key] = probeCamera;
}
return probeCamera.camera;
}
// Release unused camera keys to the pool if they are not used.
// This does not clear allocations.
public void ReleaseCamerasUnusedFor(int frameWindow, int frameCount)
{
if (m_Cache == null)
throw new ObjectDisposedException(nameof(ProbeCameraCache<K>));
if (m_Cache.Count == 0)
return;
// In case cameraKeysCache length does not matches the current cache length, we resize it:
if (m_TempCameraKeysCache.Length != m_Cache.Count)
m_TempCameraKeysCache = new K[m_Cache.Count];
// Copy keys to remove them from the dictionary (avoids collection modified while iterating error)
m_Cache.Keys.CopyTo(m_TempCameraKeysCache, 0);
foreach (var key in m_TempCameraKeysCache)
{
if (m_Cache.TryGetValue(key, out var value))
{
if (Math.Abs(frameCount - value.lastFrame) > frameWindow)
{
if (value.camera != null)
{
value.camera.name = "Unused Probe Camera";
m_CameraPool.Push(value.camera);
}
m_Cache.Remove(key);
}
}
}
}
/// Destroy all cameras in the cache and pool.
public void Clear()
{
if (m_Cache == null)
throw new ObjectDisposedException(nameof(ProbeCameraCache<K>));
foreach (var pair in m_Cache)
{
if (pair.Value.camera != null)
CoreUtils.Destroy(pair.Value.camera.gameObject);
}
m_Cache.Clear();
foreach(var camera in m_CameraPool)
{
if (camera != null)
CoreUtils.Destroy(camera.gameObject);
}
m_CameraPool.Clear();
}
public void Dispose()
{
Clear();
m_Cache = null;
m_CameraPool = null;
}
}
}