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; ///Color or depth stencil format. public GraphicsFormat format; ///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; ///Depth buffer bit depth of the format. The setter convert the bits to valid depth stencil format and sets the format. The getter gets the depth bits of the format. public DepthBits depthBufferBits { get { return (DepthBits)GraphicsFormatUtility.GetDepthBits(format); } set { if (value == DepthBits.None) { if( !GraphicsFormatUtility.IsDepthStencilFormat(format) ) return; else format = GraphicsFormat.None; } else { format = GraphicsFormatUtility.GetDepthStencilFormat((int)value); } } } ///Color format. Sets the format. The getter checks if format is a color format. Returns the format if a color format, otherwise returns GraphicsFormat.None. public GraphicsFormat colorFormat { get { return GraphicsFormatUtility.IsDepthStencilFormat(format) ? GraphicsFormat.None : format; } set { format = value; } } 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; format = (input.depthStencilFormat != GraphicsFormat.None) ? input.depthStencilFormat : 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; name = "UnNamedFromRenderTextureDescriptor"; } /// /// Hash function /// /// The texture descriptor hash. public override int GetHashCode() { var hashCode = HashFNV1A32.Create(); switch (sizeMode) { case TextureSizeMode.Explicit: hashCode.Append(width); hashCode.Append(height); break; case TextureSizeMode.Functor: if (func != null) hashCode.Append(func); break; case TextureSizeMode.Scale: hashCode.Append(scale); break; } hashCode.Append(mipMapBias); hashCode.Append(slices); hashCode.Append((int) format); hashCode.Append((int) filterMode); hashCode.Append((int) wrapMode); hashCode.Append((int) dimension); hashCode.Append((int) memoryless); hashCode.Append((int) vrUsage); hashCode.Append(anisoLevel); hashCode.Append(enableRandomWrite); hashCode.Append(useMipMap); hashCode.Append(autoGenerateMips); hashCode.Append(isShadowMap); hashCode.Append(bindTextureMS); hashCode.Append(useDynamicScale); hashCode.Append((int) msaaSamples); #if UNITY_2020_2_OR_NEWER hashCode.Append(fastMemoryDesc.inFastMemory); #endif return hashCode.value; } /// /// 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.format, desc.slices, 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.format, desc.slices, 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.format, desc.slices, 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(); } } }