using System;
using System.Collections.Generic;
using UnityEngine.Rendering;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace UnityEngine.Rendering
{
///
/// This struct contains some static helpers that can be used when converting RTid to RThandle
/// The common use case is to convert rtId to rtHandle and use the handle with other handle compatible core APIs
///
public struct RTHandleStaticHelpers
{
///
/// Static RTHandle wrapper around RenderTargetIdentifier to avoid gc.alloc
/// Set this wrapper through `RTHandleStaticHelpers.SetRTHandleStaticWrapper`
///
public static RTHandle s_RTHandleWrapper;
///
/// Set static RTHandle wrapper given a RTid. The static RTHandle wrapper is treated as external handle in RTHandleSystem
/// Get the static wrapper through `RTHandleStaticHelpers.s_RTHandleWrapper`.
///
/// Input render target identifier to be converted.
public static void SetRTHandleStaticWrapper(RenderTargetIdentifier rtId)
{
if (s_RTHandleWrapper == null)
s_RTHandleWrapper = RTHandles.Alloc(rtId);
else
s_RTHandleWrapper.SetTexture(rtId);
}
///
/// Set user managed RTHandle wrapper given a RTid. The wrapper is treated as external handle in RTHandleSystem
///
/// User managed RTHandle wrapper.
/// Input render target identifier to be set.
public static void SetRTHandleUserManagedWrapper(ref RTHandle rtWrapper, RenderTargetIdentifier rtId)
{
// User managed wrapper is null, just return here.
if (rtWrapper == null)
return;
#if DEVELOPMENT_BUILD || UNITY_EDITOR
// Check user managed RTHandle wrapper is actually a wrapper around RTid
if (rtWrapper.m_RT != null)
throw new ArgumentException($"Input wrapper must be a wrapper around RenderTargetIdentifier. Passed in wrapper contains valid RenderTexture {rtWrapper.m_RT.name} and cannot be used as wrapper.");
if (rtWrapper.m_ExternalTexture != null)
throw new ArgumentException($"Input wrapper must be a wrapper around RenderTargetIdentifier. Passed in wrapper contains valid Texture {rtWrapper.m_ExternalTexture.name} and cannot be used as wrapper.");
#endif
rtWrapper.SetTexture(rtId);
}
}
///
/// A RTHandle is a RenderTexture that scales automatically with the camera size.
/// This allows proper reutilization of RenderTexture memory when different cameras with various sizes are used during rendering.
///
///
public class RTHandle
{
internal RTHandleSystem m_Owner;
internal RenderTexture m_RT;
internal Texture m_ExternalTexture;
internal RenderTargetIdentifier m_NameID;
internal bool m_EnableMSAA = false;
internal bool m_EnableRandomWrite = false;
internal bool m_EnableHWDynamicScale = false;
internal bool m_RTHasOwnership = true;
internal string m_Name;
internal bool m_UseCustomHandleScales = false;
internal RTHandleProperties m_CustomHandleProperties;
///
/// By default, rtHandleProperties gets the global state of scalers against the global reference mode.
/// This method lets the current RTHandle use a local custom RTHandleProperties. This function is being used
/// by scalers such as TAAU and DLSS, which require to have a different resolution for color (independent of the RTHandleSystem).
///
/// Properties to set.
public void SetCustomHandleProperties(in RTHandleProperties properties)
{
m_UseCustomHandleScales = true;
m_CustomHandleProperties = properties;
}
///
/// Method that clears any custom handle property being set.
///
public void ClearCustomHandleProperties()
{
m_UseCustomHandleScales = false;
}
///
/// Scale factor applied to the RTHandle reference size.
///
public Vector2 scaleFactor { get; internal set; }
internal ScaleFunc scaleFunc;
///
/// Returns true if the RTHandle uses automatic scaling.
///
public bool useScaling { get; internal set; }
///
/// Reference size of the RTHandle System associated with the RTHandle
///
public Vector2Int referenceSize { get; internal set; }
///
/// Current properties of the RTHandle System. If a custom property has been set through SetCustomHandleProperties method, it will be used that one instead.
///
public RTHandleProperties rtHandleProperties { get { return m_UseCustomHandleScales ? m_CustomHandleProperties : m_Owner.rtHandleProperties; } }
///
/// RenderTexture associated with the RTHandle
///
public RenderTexture rt { get { return m_RT; } }
///
/// Texture associated with the RTHandle when constructed from an external Texture or RenderTexture
///
public Texture externalTexture { get { return m_ExternalTexture; } }
///
/// RenderTargetIdentifier associated with the RTHandle
///
public RenderTargetIdentifier nameID { get { return m_NameID; } }
///
/// Name of the RTHandle
///
public string name { get { return m_Name; } }
///
/// Returns true is MSAA is enabled, false otherwise.
///
public bool isMSAAEnabled { get { return m_EnableMSAA; } }
// Keep constructor private
internal RTHandle(RTHandleSystem owner)
{
m_Owner = owner;
}
///
/// Implicit conversion operator to RenderTargetIdentifier
///
/// Input RTHandle
/// RenderTargetIdentifier representation of the RTHandle.
public static implicit operator RenderTargetIdentifier(RTHandle handle)
{
return handle != null ? handle.nameID : default(RenderTargetIdentifier);
}
///
/// Implicit conversion operator to Texture
///
/// Input RTHandle
/// Texture representation of the RTHandle.
public static implicit operator Texture(RTHandle handle)
{
// If RTHandle is null then conversion should give a null Texture
if (handle == null)
return null;
Debug.Assert(handle.m_ExternalTexture != null || handle.rt != null);
return (handle.rt != null) ? handle.rt : handle.m_ExternalTexture;
}
///
/// Implicit conversion operator to RenderTexture
///
/// Input RTHandle
/// RenderTexture representation of the RTHandle.
public static implicit operator RenderTexture(RTHandle handle)
{
// If RTHandle is null then conversion should give a null RenderTexture
if (handle == null)
return null;
Debug.Assert(handle.rt != null, "RTHandle was created using a regular Texture and is used as a RenderTexture");
return handle.rt;
}
internal void SetRenderTexture(RenderTexture rt, bool transferOwnership = true)
{
m_RT = rt;
m_ExternalTexture = null;
m_RTHasOwnership = transferOwnership;
m_NameID = new RenderTargetIdentifier(rt);
}
internal void SetTexture(Texture tex)
{
m_RT = null;
m_ExternalTexture = tex;
m_NameID = new RenderTargetIdentifier(tex);
}
internal void SetTexture(RenderTargetIdentifier tex)
{
m_RT = null;
m_ExternalTexture = null;
m_NameID = tex;
}
///
/// Get the Instance ID of the RTHandle.
///
/// The RTHandle Instance ID.
public int GetInstanceID()
{
if (m_RT != null)
return m_RT.GetInstanceID();
else if (m_ExternalTexture != null)
return m_ExternalTexture.GetInstanceID();
else
return m_NameID.GetHashCode(); // No instance ID so we return the hash code.
}
///
/// Release the RTHandle
///
public void Release()
{
m_Owner.Remove(this);
if (m_RTHasOwnership)
CoreUtils.Destroy(m_RT);
m_NameID = BuiltinRenderTextureType.None;
m_RT = null;
m_ExternalTexture = null;
m_RTHasOwnership = true;
}
///
/// Return the input size, scaled by the RTHandle scale factor.
///
/// Input size
/// Input size scaled by the RTHandle scale factor.
public Vector2Int GetScaledSize(Vector2Int refSize)
{
if (!useScaling)
return refSize;
if (scaleFunc != null)
{
return scaleFunc(refSize);
}
else
{
return new Vector2Int(
x: Mathf.RoundToInt(scaleFactor.x * refSize.x),
y: Mathf.RoundToInt(scaleFactor.y * refSize.y)
);
}
}
///
/// Return the scaled size of the RTHandle.
///
/// The scaled size of the RTHandle.
public Vector2Int GetScaledSize()
{
if (!useScaling)
return referenceSize;
if (scaleFunc != null)
{
return scaleFunc(referenceSize);
}
else
{
return new Vector2Int(
x: Mathf.RoundToInt(scaleFactor.x * referenceSize.x),
y: Mathf.RoundToInt(scaleFactor.y * referenceSize.y)
);
}
}
#if UNITY_2020_2_OR_NEWER
///
/// Switch the render target to fast memory on platform that have it.
///
/// Command buffer used for rendering.
/// How much of the render target is to be switched into fast memory (between 0 and 1).
/// Flag to determine what parts of the render target is spilled if not fully resident in fast memory.
/// Whether the content of render target are copied or not when switching to fast memory.
public void SwitchToFastMemory(CommandBuffer cmd,
float residencyFraction = 1.0f,
FastMemoryFlags flags = FastMemoryFlags.SpillTop,
bool copyContents = false
)
{
residencyFraction = Mathf.Clamp01(residencyFraction);
cmd.SwitchIntoFastMemory(m_RT, flags, residencyFraction, copyContents);
}
///
/// Switch the render target to fast memory on platform that have it and copies the content.
///
/// Command buffer used for rendering.
/// How much of the render target is to be switched into fast memory (between 0 and 1).
/// Flag to determine what parts of the render target is spilled if not fully resident in fast memory.
public void CopyToFastMemory(CommandBuffer cmd,
float residencyFraction = 1.0f,
FastMemoryFlags flags = FastMemoryFlags.SpillTop
)
{
SwitchToFastMemory(cmd, residencyFraction, flags, copyContents: true);
}
///
/// Switch out the render target from fast memory back to main memory on platforms that have fast memory.
///
/// Command buffer used for rendering.
/// Whether the content of render target are copied or not when switching out fast memory.
public void SwitchOutFastMemory(CommandBuffer cmd, bool copyContents = true)
{
cmd.SwitchOutOfFastMemory(m_RT, copyContents);
}
#endif
}
}