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 } }