228 lines
7.8 KiB
C#
228 lines
7.8 KiB
C#
using System;
|
|
using Unity.Mathematics;
|
|
|
|
namespace UnityEngine.Rendering.Universal
|
|
{
|
|
// NOTE: This might become SRPCameraHistory for unified history access in the future.
|
|
|
|
/// <summary>
|
|
/// URP camera history texture manager.
|
|
/// </summary>
|
|
public class UniversalCameraHistory : ICameraHistoryReadAccess, ICameraHistoryWriteAccess, IPerFrameHistoryAccessTracker, IDisposable
|
|
{
|
|
/// <summary>
|
|
/// Number of frames to consider history valid.
|
|
/// </summary>
|
|
const int k_ValidVersionCount = 2; // current frame + previous frame
|
|
|
|
private static uint s_TypeCount = 0;
|
|
private static class TypeId<T>
|
|
{
|
|
public static uint value = s_TypeCount++;
|
|
}
|
|
|
|
private struct Item
|
|
{
|
|
public ContextItem storage;
|
|
|
|
// The last tick the type was requested.
|
|
public int requestVersion;
|
|
|
|
// The last tick the type was written to.
|
|
public int writeVersion;
|
|
|
|
public void Reset()
|
|
{
|
|
storage?.Reset();
|
|
requestVersion = -k_ValidVersionCount; // NOTE: must be invalid on frame 0
|
|
writeVersion = -k_ValidVersionCount;
|
|
}
|
|
}
|
|
private Item[] m_Items = new Item[32];
|
|
private int m_Version = 0;
|
|
|
|
// A central storage for camera history textures.
|
|
private BufferedRTHandleSystem m_HistoryTextures = new BufferedRTHandleSystem();
|
|
|
|
#region SrpApi
|
|
|
|
/// <summary>
|
|
/// Request access to a history item.
|
|
/// </summary>
|
|
/// <typeparam name="Type">Type of the history item.</typeparam>
|
|
public void RequestAccess<Type>() where Type : ContextItem
|
|
{
|
|
uint index = TypeId<Type>.value;
|
|
|
|
// Resize
|
|
if(index >= m_Items.Length)
|
|
{
|
|
var items = new Item[math.max(math.ceilpow2(s_TypeCount), m_Items.Length * 2)];
|
|
for (var i = 0; i < m_Items.Length; i++)
|
|
{
|
|
items[i] = m_Items[i];
|
|
}
|
|
m_Items = items;
|
|
}
|
|
|
|
m_Items[index].requestVersion = m_Version;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtain read access to a history item.
|
|
/// Valid only if the item was requested and written this or the previous frame.
|
|
/// </summary>
|
|
/// <typeparam name="Type">Type of the history item.</typeparam>
|
|
/// <returns>Instance of the history item if valid. Null otherwise.</returns>
|
|
public Type GetHistoryForRead<Type>() where Type : ContextItem
|
|
{
|
|
uint index = TypeId<Type>.value;
|
|
|
|
if (index >= m_Items.Length)
|
|
return null;
|
|
|
|
// If the Type wasn't written in previous or this frame, return null.
|
|
// The Type design is expected to handle, current/previous/Nth frame history access via BufferedRTHandleSystem.
|
|
if (!IsValid((int)index))
|
|
return null;
|
|
|
|
return (Type)m_Items[index].storage;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if a type was requested this frame.
|
|
/// </summary>
|
|
/// <typeparam name="Type">Type of the history item.</typeparam>
|
|
/// <returns>True if an active request exists. False otherwise.</returns>
|
|
public bool IsAccessRequested<Type>() where Type : ContextItem
|
|
{
|
|
uint index = TypeId<Type>.value;
|
|
|
|
if (index >= m_Items.Length)
|
|
return false;
|
|
|
|
return IsValidRequest((int)index);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtain write access to a history item.
|
|
/// Valid only if the item was requested this or the previous frame.
|
|
/// Write access implies that the contents of the history item must be written.
|
|
/// </summary>
|
|
/// <typeparam name="Type">Type of the history item.</typeparam>
|
|
/// <returns>Instance of the history item if valid. Null otherwise.</returns>
|
|
public Type GetHistoryForWrite<Type>() where Type : ContextItem, new()
|
|
{
|
|
uint index = TypeId<Type>.value;
|
|
|
|
if (index >= m_Items.Length)
|
|
return null;
|
|
|
|
if (!IsValidRequest((int)index)) // If the request is too old, return null.
|
|
return null;
|
|
|
|
// Create Type instance on the first use
|
|
if (m_Items[index].storage == null)
|
|
{
|
|
ref var i = ref m_Items[index];
|
|
i.storage = new Type();
|
|
|
|
// If the convenience base class for BufferedRTHandleSystem is used, set the owner.
|
|
if (i.storage is CameraHistoryItem hi)
|
|
hi.OnCreate(m_HistoryTextures, index);
|
|
}
|
|
|
|
// Assume the write for GetForWrite is done correctly by the caller.
|
|
m_Items[index].writeVersion = m_Version;
|
|
|
|
ContextItem item = m_Items[index].storage;
|
|
return (Type)item;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if a type was written this frame.
|
|
/// </summary>
|
|
/// <typeparam name="Type">Type of the history item.</typeparam>
|
|
/// <returns>True if write access was obtained this frame. False otherwise.</returns>
|
|
public bool IsWritten<Type>() where Type : ContextItem
|
|
{
|
|
uint index = TypeId<Type>.value;
|
|
|
|
if (index >= m_Items.Length)
|
|
return false;
|
|
|
|
return m_Items[index].writeVersion == m_Version;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register external type request callbacks to this event.
|
|
/// </summary>
|
|
public event ICameraHistoryReadAccess.HistoryRequestDelegate OnGatherHistoryRequests;
|
|
|
|
#endregion
|
|
#region UrpApi
|
|
|
|
internal UniversalCameraHistory()
|
|
{
|
|
// Init items with invalid versions.
|
|
for(int i = 0; i < m_Items.Length; i++)
|
|
m_Items[i].Reset();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Release all camera history textures on the GPU.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
for (int i = 0; i < m_Items.Length; i++)
|
|
m_Items[i].Reset();
|
|
|
|
m_HistoryTextures.ReleaseAll();
|
|
}
|
|
|
|
// Query which types are needed for the registered system.
|
|
internal void GatherHistoryRequests()
|
|
{
|
|
OnGatherHistoryRequests?.Invoke(this);
|
|
}
|
|
|
|
// A type is valid if it was requested this or the last frame.
|
|
// Requesting access in the leaf classes, means we might be 1 frame late.
|
|
private bool IsValidRequest(int i)
|
|
{
|
|
return ((m_Version - m_Items[i].requestVersion) < k_ValidVersionCount);
|
|
}
|
|
|
|
// A type is valid if it was requested and written to this or the last frame.
|
|
// Requesting access in the leaf classes, means we might be 1 frame late.
|
|
// NOTE: BufferedRTHandleSystem technically supports history of N length.
|
|
// We might need to keep the history for N frames.
|
|
// For now we expect that active history has the previous frame written.
|
|
private bool IsValid(int i)
|
|
{
|
|
return ((m_Version - m_Items[i].writeVersion) < k_ValidVersionCount);
|
|
}
|
|
|
|
// Garbage collect old unused type instances and Reset them. The user is expected to free any GPU resources.
|
|
internal void ReleaseUnusedHistory()
|
|
{
|
|
for (int i = 0; i < m_Items.Length; i++)
|
|
{
|
|
if (!IsValidRequest(i) && !IsValid(i))
|
|
m_Items[i].Reset();
|
|
}
|
|
|
|
// After collecting stale Types, start a new generation.
|
|
m_Version++;
|
|
}
|
|
|
|
// Set the camera reference size for all history textures.
|
|
internal void SwapAndSetReferenceSize(int cameraWidth, int cameraHeight)
|
|
{
|
|
m_HistoryTextures.SwapAndSetReferenceSize(cameraWidth, cameraHeight);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|