using System; using System.Diagnostics; using System.Runtime.CompilerServices; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.Scripting.APIUpdating; namespace UnityEngine.Rendering.RenderGraphModule { internal struct TextureAccess { public TextureHandle textureHandle; public int mipLevel; public int depthSlice; public AccessFlags flags; public TextureAccess(TextureHandle handle, AccessFlags flags, int mipLevel, int depthSlice) { this.textureHandle = handle; this.flags = flags; this.mipLevel = mipLevel; this.depthSlice = depthSlice; } } /// /// An abstract handle representing a texture resource as known by one particular record + execute of the render graph. /// TextureHandles should not be used outside of the context of a render graph execution. /// /// A render graph needs to do additional state tracking on texture resources (lifetime, how is it used,...) to enable /// this all textures relevant to the render graph need to be make known to it. A texture handle specifies such a texture as /// known to the render graph. /// /// It is important to understand that a render graph texture handle does not necessarily represent an actual texture. For example /// textures could be created the render graph that are only referenced by passes that are later culled when executing the graph. /// Such textures would never be allocated as actual RenderTextures. /// /// Texture handles are only relevant to one particular record+execute phase of the render graph. After execution all texture /// handles are invalidated. The system will catch texture handles from a different execution of the render graph but still /// users should be careful to avoid keeping texture handles around from other render graph executions. /// /// Texture handles do not need to be disposed/freed (they are auto-invalidated at the end of graph execution). The RenderTextures they represent /// are either freed by the render graph internally (when the handle was acquired through RenderGraph.CreateTexture) or explicitly managed by /// some external system (when acquired through RenderGraph.ImportTexture). /// /// [DebuggerDisplay("Texture ({handle.index})")] [MovedFrom(true, "UnityEngine.Experimental.Rendering.RenderGraphModule", "UnityEngine.Rendering.RenderGraphModule")] public struct TextureHandle { private static TextureHandle s_NullHandle = new TextureHandle(); /// /// Returns a null texture handle /// /// A null texture handle. public static TextureHandle nullHandle { get { return s_NullHandle; } } internal ResourceHandle handle; private bool builtin; internal TextureHandle(in ResourceHandle h) { handle = h; builtin = false; } internal TextureHandle(int handle, bool shared = false, bool builtin = false) { this.handle = new ResourceHandle(handle, RenderGraphResourceType.Texture, shared); this.builtin = builtin; } /// /// Cast to RenderTargetIdentifier /// /// Input TextureHandle. /// Resource as a RenderTargetIdentifier. public static implicit operator RenderTargetIdentifier(TextureHandle texture) => texture.IsValid() ? RenderGraphResourceRegistry.current.GetTexture(texture) : default(RenderTargetIdentifier); /// /// Cast to Texture /// /// Input TextureHandle. /// Resource as a Texture. public static implicit operator Texture(TextureHandle texture) => texture.IsValid() ? RenderGraphResourceRegistry.current.GetTexture(texture) : null; /// /// Cast to RenderTexture /// /// Input TextureHandle. /// Resource as a RenderTexture. public static implicit operator RenderTexture(TextureHandle texture) => texture.IsValid() ? RenderGraphResourceRegistry.current.GetTexture(texture) : null; /// /// Cast to RTHandle /// /// Input TextureHandle. /// Resource as a RTHandle. public static implicit operator RTHandle(TextureHandle texture) => texture.IsValid() ? RenderGraphResourceRegistry.current.GetTexture(texture) : null; /// /// Return true if the handle is valid. /// /// True if the handle is valid. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool IsValid() => handle.IsValid(); /// /// Return true if the handle is a builtin handle managed by RenderGraph internally. /// /// True if the handle is a builtin handle. [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool IsBuiltin() => this.builtin; /// /// Get the Descriptor of the texture. This simply calls RenderGraph.GetTextureDesc but is more easily discoverable through auto complete. /// /// The rendergraph instance that was used to create the texture on. Texture handles are a lightweight object, all information is stored on the RenderGraph itself. /// The texture descriptor for the given texture handle. public TextureDesc GetDescriptor(RenderGraph renderGraph) { return renderGraph.GetTextureDesc(this); } } /// /// The mode that determines the size of a Texture. /// public enum TextureSizeMode { ///Explicit size. Explicit, ///Size automatically scaled by a Vector. Scale, ///Size automatically scaled by a Functor. Functor } #if UNITY_2020_2_OR_NEWER /// /// Subset of the texture desc containing information for fast memory allocation (when platform supports it) /// public struct FastMemoryDesc { ///Whether the texture will be in fast memory. public bool inFastMemory; ///Flag to determine what parts of the render target is spilled if not fully resident in fast memory. public FastMemoryFlags flags; ///How much of the render target is to be switched into fast memory (between 0 and 1). public float residencyFraction; } #endif /// /// Descriptor used to create texture resources /// public struct TextureDesc { ///Texture sizing mode. public TextureSizeMode sizeMode; ///Texture width. public int width; ///Texture height. public int height; ///Number of texture slices.. public int slices; ///Texture scale. public Vector2 scale; ///Texture scale function. public ScaleFunc func; ///Depth buffer bit depth. public DepthBits depthBufferBits; ///Color format. public GraphicsFormat colorFormat; ///Filtering mode. public FilterMode filterMode; ///Addressing mode. public TextureWrapMode wrapMode; ///Texture dimension. public TextureDimension dimension; ///Enable random UAV read/write on the texture. public bool enableRandomWrite; ///Texture needs mip maps. public bool useMipMap; ///Automatically generate mip maps. public bool autoGenerateMips; ///Texture is a shadow map. public bool isShadowMap; ///Anisotropic filtering level. public int anisoLevel; ///Mip map bias. public float mipMapBias; ///Number of MSAA samples. public MSAASamples msaaSamples; ///Bind texture multi sampled. public bool bindTextureMS; ///[See Dynamic Resolution documentation](https://docs.unity3d.com/Manual/DynamicResolution.html) public bool useDynamicScale; ///[See Dynamic Resolution documentation](https://docs.unity3d.com/Manual/DynamicResolution.html) public bool useDynamicScaleExplicit; ///Memory less flag. public RenderTextureMemoryless memoryless; ///Special treatment of the VR eye texture used in stereoscopic rendering. public VRTextureUsage vrUsage; ///Texture name. public string name; #if UNITY_2020_2_OR_NEWER ///Descriptor to determine how the texture will be in fast memory on platform that supports it. public FastMemoryDesc fastMemoryDesc; #endif ///Determines whether the texture will fallback to a black texture if it is read without ever writing to it. public bool fallBackToBlackTexture; /// ///If all passes writing to a texture are culled by Dynamic Render Pass Culling, it will automatically fallback to a similar preallocated texture. ///Set this to true to force the allocation. /// public bool disableFallBackToImportedTexture; // Initial state. Those should not be used in the hash ///Texture needs to be cleared on first use. public bool clearBuffer; ///Clear color. public Color clearColor; ///Texture needs to be discarded on last use. public bool discardBuffer; void InitDefaultValues(bool dynamicResolution, bool xrReady) { useDynamicScale = dynamicResolution; vrUsage = VRTextureUsage.None; // XR Ready if (xrReady) { slices = TextureXR.slices; dimension = TextureXR.dimension; } else { slices = 1; dimension = TextureDimension.Tex2D; } discardBuffer = false; } /// /// TextureDesc constructor for a texture using explicit size /// /// Texture width /// Texture height /// Use dynamic resolution /// Set this to true if the Texture is a render texture in an XR setting. public TextureDesc(int width, int height, bool dynamicResolution = false, bool xrReady = false) : this() { // Size related init sizeMode = TextureSizeMode.Explicit; this.width = width; this.height = height; // Important default values not handled by zero construction in this() msaaSamples = MSAASamples.None; InitDefaultValues(dynamicResolution, xrReady); } /// /// TextureDesc constructor for a texture using a fixed scaling /// /// RTHandle scale used for this texture /// Use dynamic resolution /// Set this to true if the Texture is a render texture in an XR setting. public TextureDesc(Vector2 scale, bool dynamicResolution = false, bool xrReady = false) : this() { // Size related init sizeMode = TextureSizeMode.Scale; this.scale = scale; // Important default values not handled by zero construction in this() msaaSamples = MSAASamples.None; dimension = TextureDimension.Tex2D; InitDefaultValues(dynamicResolution, xrReady); } /// /// TextureDesc constructor for a texture using a functor for scaling /// /// Function used to determine the texture size /// Use dynamic resolution /// Set this to true if the Texture is a render texture in an XR setting. public TextureDesc(ScaleFunc func, bool dynamicResolution = false, bool xrReady = false) : this() { // Size related init sizeMode = TextureSizeMode.Functor; this.func = func; // Important default values not handled by zero construction in this() msaaSamples = MSAASamples.None; dimension = TextureDimension.Tex2D; InitDefaultValues(dynamicResolution, xrReady); } /// /// Copy constructor /// /// The TextureDesc instance to copy from. public TextureDesc(TextureDesc input) { this = input; } /// /// Do a best effort conversion from a RenderTextureDescriptor to a TextureDesc. This tries to initialize a descriptor to be as close as possible to the given render texture descriptor but there might be subtle differences when creating /// render graph textures using this TextureDesc due to the underlying RTHandle system. /// Some parameters of the TextureDesc (like name and filtering modes) are not present in the RenderTextureDescriptor for these the returned TextureDesc will contain plausible default values. /// /// The texture descriptor to create a TextureDesc from public TextureDesc(RenderTextureDescriptor input) { sizeMode = TextureSizeMode.Explicit; width = input.width; height = input.height; slices = input.volumeDepth; scale = Vector2.one; func = null; depthBufferBits = (DepthBits)input.depthBufferBits; colorFormat = input.graphicsFormat; filterMode = FilterMode.Bilinear; wrapMode = TextureWrapMode.Clamp; dimension = input.dimension; enableRandomWrite = input.enableRandomWrite; useMipMap = input.useMipMap; autoGenerateMips = input.autoGenerateMips; isShadowMap = (input.shadowSamplingMode != ShadowSamplingMode.None); anisoLevel = 1; mipMapBias = 0; msaaSamples = (MSAASamples)input.msaaSamples; bindTextureMS = input.bindMS; useDynamicScale = input.useDynamicScale; useDynamicScaleExplicit = false; memoryless = input.memoryless; vrUsage = input.vrUsage; name = "UnNamedFromRenderTextureDescriptor"; fastMemoryDesc = new FastMemoryDesc(); fastMemoryDesc.inFastMemory = false; fallBackToBlackTexture = false; disableFallBackToImportedTexture = true; clearBuffer = true; clearColor = Color.black; discardBuffer = false; } /// /// Do a best effort conversion from a RenderTexture to a TextureDesc. This tries to initialize a descriptor to be as close as possible to the given render texture but there might be subtle differences when creating /// render graph textures using this TextureDesc due to the underlying RTHandle system. /// /// The texture to create a TextureDesc from public TextureDesc(RenderTexture input) : this(input.descriptor) { filterMode = input.filterMode; wrapMode = input.wrapMode; anisoLevel = input.anisoLevel; mipMapBias = input.mipMapBias; this.name = "UnNamedFromRenderTextureDescriptor"; } /// /// Hash function /// /// The texture descriptor hash. public override int GetHashCode() { int hashCode = 17; unchecked { switch (sizeMode) { case TextureSizeMode.Explicit: hashCode = hashCode * 23 + width; hashCode = hashCode * 23 + height; break; case TextureSizeMode.Functor: if (func != null) hashCode = hashCode * 23 + func.GetHashCode(); break; case TextureSizeMode.Scale: hashCode = hashCode * 23 + scale.x.GetHashCode(); hashCode = hashCode * 23 + scale.y.GetHashCode(); break; } hashCode = hashCode * 23 + mipMapBias.GetHashCode(); hashCode = hashCode * 23 + slices; hashCode = hashCode * 23 + (int)depthBufferBits; hashCode = hashCode * 23 + (int)colorFormat; hashCode = hashCode * 23 + (int)filterMode; hashCode = hashCode * 23 + (int)wrapMode; hashCode = hashCode * 23 + (int)dimension; hashCode = hashCode * 23 + (int)memoryless; hashCode = hashCode * 23 + (int)vrUsage; hashCode = hashCode * 23 + anisoLevel; hashCode = hashCode * 23 + (enableRandomWrite ? 1 : 0); hashCode = hashCode * 23 + (useMipMap ? 1 : 0); hashCode = hashCode * 23 + (autoGenerateMips ? 1 : 0); hashCode = hashCode * 23 + (isShadowMap ? 1 : 0); hashCode = hashCode * 23 + (bindTextureMS ? 1 : 0); hashCode = hashCode * 23 + (useDynamicScale ? 1 : 0); hashCode = hashCode * 23 + (int)msaaSamples; #if UNITY_2020_2_OR_NEWER hashCode = hashCode * 23 + (fastMemoryDesc.inFastMemory ? 1 : 0); #endif } return hashCode; } /// /// Calculate the final size of the texture descriptor in pixels. This takes into account the sizeMode set for this descriptor. /// For the automatically scaled sizes the size will be relative to the RTHandle reference size SetReferenceSize. /// /// The calculated size. /// Thrown if the texture descriptor's size mode falls outside the expected range. public Vector2Int CalculateFinalDimensions() { return sizeMode switch { TextureSizeMode.Explicit => new Vector2Int(width, height), TextureSizeMode.Scale => RTHandles.CalculateDimensions(scale), TextureSizeMode.Functor => RTHandles.CalculateDimensions(func), _ => throw new ArgumentOutOfRangeException() }; } } [DebuggerDisplay("TextureResource ({desc.name})")] class TextureResource : RenderGraphResource { static int m_TextureCreationIndex; public override string GetName() { if (imported && !shared) return graphicsResource != null ? graphicsResource.name : "null resource"; else return desc.name; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetDescHashCode() { return desc.GetHashCode(); } public override void CreateGraphicsResource() { var name = GetName(); // Textures are going to be reused under different aliases along the frame so we can't provide a specific name upon creation. // The name in the desc is going to be used for debugging purpose and render graph visualization. if (name == "") name = $"RenderGraphTexture_{m_TextureCreationIndex++}"; switch (desc.sizeMode) { case TextureSizeMode.Explicit: graphicsResource = RTHandles.Alloc(desc.width, desc.height, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.msaaSamples, desc.bindTextureMS, desc.useDynamicScale, desc.useDynamicScaleExplicit, desc.memoryless, desc.vrUsage, name); break; case TextureSizeMode.Scale: graphicsResource = RTHandles.Alloc(desc.scale, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.msaaSamples, desc.bindTextureMS, desc.useDynamicScale, desc.useDynamicScaleExplicit, desc.memoryless, desc.vrUsage, name); break; case TextureSizeMode.Functor: graphicsResource = RTHandles.Alloc(desc.func, desc.slices, desc.depthBufferBits, desc.colorFormat, desc.filterMode, desc.wrapMode, desc.dimension, desc.enableRandomWrite, desc.useMipMap, desc.autoGenerateMips, desc.isShadowMap, desc.anisoLevel, desc.mipMapBias, desc.msaaSamples, desc.bindTextureMS, desc.useDynamicScale, desc.useDynamicScaleExplicit, desc.memoryless, desc.vrUsage, name); break; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void UpdateGraphicsResource() { if (graphicsResource != null) graphicsResource.m_Name = GetName(); } public override void ReleaseGraphicsResource() { if (graphicsResource != null) graphicsResource.Release(); base.ReleaseGraphicsResource(); } public override void LogCreation(RenderGraphLogger logger) { logger.LogLine($"Created Texture: {desc.name} (Cleared: {desc.clearBuffer})"); } public override void LogRelease(RenderGraphLogger logger) { logger.LogLine($"Released Texture: {desc.name}"); } } class TexturePool : RenderGraphResourcePool { protected override void ReleaseInternalResource(RTHandle res) { res.Release(); } protected override string GetResourceName(in RTHandle res) { return res.rt.name; } protected override long GetResourceSize(in RTHandle res) { return Profiling.Profiler.GetRuntimeMemorySizeLong(res.rt); } override protected string GetResourceTypeName() { return "Texture"; } override protected int GetSortIndex(RTHandle res) { return res.GetInstanceID(); } } }