using System; using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.RenderGraphModule; using System.Diagnostics; namespace UnityEngine.Rendering.Universal { /// /// Contains properties and helper functions that you can use when rendering. /// public static class RenderingUtils { static List m_LegacyShaderPassNames = new List { new ShaderTagId("Always"), new ShaderTagId("ForwardBase"), new ShaderTagId("PrepassBase"), new ShaderTagId("Vertex"), new ShaderTagId("VertexLMRGBM"), new ShaderTagId("VertexLM"), }; static AttachmentDescriptor s_EmptyAttachment = new AttachmentDescriptor(GraphicsFormat.None); internal static AttachmentDescriptor emptyAttachment { get { return s_EmptyAttachment; } } static Mesh s_FullscreenMesh = null; /// /// Returns a mesh that you can use with to render full-screen effects. /// [Obsolete("Use Blitter.BlitCameraTexture instead of CommandBuffer.DrawMesh(fullscreenMesh, ...)")] // TODO OBSOLETE: need to fix the URP test failures when bumping public static Mesh fullscreenMesh { get { if (s_FullscreenMesh != null) return s_FullscreenMesh; float topV = 1.0f; float bottomV = 0.0f; s_FullscreenMesh = new Mesh { name = "Fullscreen Quad" }; s_FullscreenMesh.SetVertices(new List { new Vector3(-1.0f, -1.0f, 0.0f), new Vector3(-1.0f, 1.0f, 0.0f), new Vector3(1.0f, -1.0f, 0.0f), new Vector3(1.0f, 1.0f, 0.0f) }); s_FullscreenMesh.SetUVs(0, new List { new Vector2(0.0f, bottomV), new Vector2(0.0f, topV), new Vector2(1.0f, bottomV), new Vector2(1.0f, topV) }); s_FullscreenMesh.SetIndices(new[] { 0, 1, 2, 2, 1, 3 }, MeshTopology.Triangles, 0, false); s_FullscreenMesh.UploadMeshData(true); return s_FullscreenMesh; } } internal static bool useStructuredBuffer { // There are some performance issues with StructuredBuffers in some platforms. // We fallback to UBO in those cases. get { // TODO: For now disabling SSBO until figure out Vulkan binding issues. // When enabling this also enable USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA in shader side in Input.hlsl return false; // We don't use SSBO in D3D because we can't figure out without adding shader variants if platforms is D3D10. //GraphicsDeviceType deviceType = SystemInfo.graphicsDeviceType; //return !Application.isMobilePlatform && // (deviceType == GraphicsDeviceType.Metal || deviceType == GraphicsDeviceType.Vulkan || // deviceType == GraphicsDeviceType.PlayStation4 || deviceType == GraphicsDeviceType.PlayStation5 || deviceType == GraphicsDeviceType.XboxOne); } } internal static bool SupportsLightLayers(GraphicsDeviceType type) { return true; } static Material s_ErrorMaterial; static Material errorMaterial { get { if (s_ErrorMaterial == null) { // TODO: When importing project, AssetPreviewUpdater::CreatePreviewForAsset will be called multiple times. // This might be in a point that some resources required for the pipeline are not finished importing yet. // Proper fix is to add a fence on asset import. try { s_ErrorMaterial = new Material(Shader.Find("Hidden/Universal Render Pipeline/FallbackError")); } catch { } } return s_ErrorMaterial; } } /// /// Set view and projection matrices. /// This function will set UNITY_MATRIX_V, UNITY_MATRIX_P, UNITY_MATRIX_VP to given view and projection matrices. /// If setInverseMatrices is set to true this function will also set UNITY_MATRIX_I_V and UNITY_MATRIX_I_VP. /// /// CommandBuffer to submit data to GPU. /// View matrix to be set. /// Projection matrix to be set. /// Set this to true if you also need to set inverse camera matrices. public static void SetViewAndProjectionMatrices(CommandBuffer cmd, Matrix4x4 viewMatrix, Matrix4x4 projectionMatrix, bool setInverseMatrices) { SetViewAndProjectionMatrices(CommandBufferHelpers.GetRasterCommandBuffer(cmd), viewMatrix, projectionMatrix, setInverseMatrices); } internal static void SetViewAndProjectionMatrices(RasterCommandBuffer cmd, Matrix4x4 viewMatrix, Matrix4x4 projectionMatrix, bool setInverseMatrices) { Matrix4x4 viewAndProjectionMatrix = projectionMatrix * viewMatrix; cmd.SetGlobalMatrix(ShaderPropertyId.viewMatrix, viewMatrix); cmd.SetGlobalMatrix(ShaderPropertyId.projectionMatrix, projectionMatrix); cmd.SetGlobalMatrix(ShaderPropertyId.viewAndProjectionMatrix, viewAndProjectionMatrix); if (setInverseMatrices) { Matrix4x4 inverseViewMatrix = Matrix4x4.Inverse(viewMatrix); Matrix4x4 inverseProjectionMatrix = Matrix4x4.Inverse(projectionMatrix); Matrix4x4 inverseViewProjection = inverseViewMatrix * inverseProjectionMatrix; cmd.SetGlobalMatrix(ShaderPropertyId.inverseViewMatrix, inverseViewMatrix); cmd.SetGlobalMatrix(ShaderPropertyId.inverseProjectionMatrix, inverseProjectionMatrix); cmd.SetGlobalMatrix(ShaderPropertyId.inverseViewAndProjectionMatrix, inverseViewProjection); } } //TODO FrameData: Merge these two SetScaleBiasRt() functions internal static void SetScaleBiasRt(RasterCommandBuffer cmd, in UniversalCameraData cameraData, RTHandle rTHandle) { // SetRenderTarget has logic to flip projection matrix when rendering to render texture. Flip the uv to account for that case. bool isCameraColorFinalTarget = (cameraData.cameraType == CameraType.Game && rTHandle.nameID == BuiltinRenderTextureType.CameraTarget && cameraData.camera.targetTexture == null); bool yflip = !isCameraColorFinalTarget; float flipSign = yflip ? -1.0f : 1.0f; Vector4 scaleBiasRt = (flipSign < 0.0f) ? new Vector4(flipSign, 1.0f, -1.0f, 1.0f) : new Vector4(flipSign, 0.0f, 1.0f, 1.0f); cmd.SetGlobalVector(Shader.PropertyToID("_ScaleBiasRt"), scaleBiasRt); } internal static void SetScaleBiasRt(RasterCommandBuffer cmd, in RenderingData renderingData) { var renderer = renderingData.cameraData.renderer; // SetRenderTarget has logic to flip projection matrix when rendering to render texture. Flip the uv to account for that case. CameraData cameraData = renderingData.cameraData; // Disable obsolete warning for internal usage #pragma warning disable CS0618 bool isCameraColorFinalTarget = (cameraData.cameraType == CameraType.Game && renderer.cameraColorTargetHandle.nameID == BuiltinRenderTextureType.CameraTarget && cameraData.camera.targetTexture == null); #pragma warning restore CS0618 bool yflip = !isCameraColorFinalTarget; float flipSign = yflip ? -1.0f : 1.0f; Vector4 scaleBiasRt = (flipSign < 0.0f) ? new Vector4(flipSign, 1.0f, -1.0f, 1.0f) : new Vector4(flipSign, 0.0f, 1.0f, 1.0f); cmd.SetGlobalVector(Shader.PropertyToID("_ScaleBiasRt"), scaleBiasRt); } internal static void Blit(CommandBuffer cmd, RTHandle source, Rect viewport, RTHandle destination, RenderBufferLoadAction loadAction, RenderBufferStoreAction storeAction, ClearFlag clearFlag, Color clearColor, Material material, int passIndex = 0) { Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one; CoreUtils.SetRenderTarget(cmd, destination, loadAction, storeAction, ClearFlag.None, Color.clear); cmd.SetViewport(viewport); Blitter.BlitTexture(cmd, source, viewportScale, material, passIndex); } internal static void Blit(CommandBuffer cmd, RTHandle source, Rect viewport, RTHandle destinationColor, RenderBufferLoadAction colorLoadAction, RenderBufferStoreAction colorStoreAction, RTHandle destinationDepthStencil, RenderBufferLoadAction depthStencilLoadAction, RenderBufferStoreAction depthStencilStoreAction, ClearFlag clearFlag, Color clearColor, Material material, int passIndex = 0) { Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one; CoreUtils.SetRenderTarget(cmd, destinationColor, colorLoadAction, colorStoreAction, destinationDepthStencil, depthStencilLoadAction, depthStencilStoreAction, clearFlag, clearColor); // implicit depth=1.0f stencil=0x0 cmd.SetViewport(viewport); Blitter.BlitTexture(cmd, source, viewportScale, material, passIndex); } internal static void FinalBlit( CommandBuffer cmd, UniversalCameraData cameraData, RTHandle source, RTHandle destination, RenderBufferLoadAction loadAction, RenderBufferStoreAction storeAction, Material material, int passIndex) { bool isRenderToBackBufferTarget = !cameraData.isSceneViewCamera; #if ENABLE_VR && ENABLE_XR_MODULE if (cameraData.xr.enabled) isRenderToBackBufferTarget = new RenderTargetIdentifier(destination.nameID, 0, CubemapFace.Unknown, -1) == new RenderTargetIdentifier(cameraData.xr.renderTarget, 0, CubemapFace.Unknown, -1); #endif Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one; // We y-flip if // 1) we are blitting from render texture to back buffer(UV starts at bottom) and // 2) renderTexture starts UV at top bool yflip = isRenderToBackBufferTarget && cameraData.targetTexture == null && SystemInfo.graphicsUVStartsAtTop; Vector4 scaleBias = yflip ? new Vector4(viewportScale.x, -viewportScale.y, 0, viewportScale.y) : new Vector4(viewportScale.x, viewportScale.y, 0, 0); CoreUtils.SetRenderTarget(cmd, destination, loadAction, storeAction, ClearFlag.None, Color.clear); if (isRenderToBackBufferTarget) cmd.SetViewport(cameraData.pixelRect); // cmd.Blit must be used in Scene View for wireframe mode to make the full screen draw with fill mode // This branch of the if statement must be removed for render graph and the new command list with a novel way of using Blitter with fill mode if (GL.wireframe && cameraData.isSceneViewCamera) { // This set render target is necessary so we change the LOAD state to DontCare. cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget, loadAction, storeAction, // color RenderBufferLoadAction.DontCare, RenderBufferStoreAction.DontCare); // depth // Necessary to disable the wireframe here, since Vulkan is handling the wireframe differently // to handle the Terrain "Draw Instanced" scenario (Ono: case-1205332). if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Vulkan) { cmd.SetWireframe(false); cmd.Blit(source, destination); cmd.SetWireframe(true); } else { cmd.Blit(source, destination); } } else if (source.rt == null) Blitter.BlitTexture(cmd, source.nameID, scaleBias, material, passIndex); // Obsolete usage of RTHandle aliasing a RenderTargetIdentifier else Blitter.BlitTexture(cmd, source, scaleBias, material, passIndex); } // This is used to render materials that contain built-in shader passes not compatible with URP. // It will render those legacy passes with error/pink shader. [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")] internal static void CreateRendererParamsObjectsWithError(ref CullingResults cullResults, Camera camera, FilteringSettings filterSettings, SortingCriteria sortFlags, ref RendererListParams param) { SortingSettings sortingSettings = new SortingSettings(camera) { criteria = sortFlags }; DrawingSettings errorSettings = new DrawingSettings(m_LegacyShaderPassNames[0], sortingSettings) { perObjectData = PerObjectData.None, overrideMaterial = errorMaterial, overrideMaterialPassIndex = 0 }; for (int i = 1; i < m_LegacyShaderPassNames.Count; ++i) errorSettings.SetShaderPassName(i, m_LegacyShaderPassNames[i]); param = new RendererListParams(cullResults, errorSettings, filterSettings); } [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")] internal static void CreateRendererListObjectsWithError(ScriptableRenderContext context, ref CullingResults cullResults, Camera camera, FilteringSettings filterSettings, SortingCriteria sortFlags, ref RendererList rl) { // TODO: When importing project, AssetPreviewUpdater::CreatePreviewForAsset will be called multiple times. // This might be in a point that some resources required for the pipeline are not finished importing yet. // Proper fix is to add a fence on asset import. if (errorMaterial == null) { rl = RendererList.nullRendererList; return; } RendererListParams param = new RendererListParams(); CreateRendererParamsObjectsWithError(ref cullResults, camera, filterSettings, sortFlags, ref param); rl = context.CreateRendererList(ref param); } // This is used to render materials that contain built-in shader passes not compatible with URP. // It will render those legacy passes with error/pink shader. [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")] internal static void CreateRendererListObjectsWithError(RenderGraph renderGraph, ref CullingResults cullResults, Camera camera, FilteringSettings filterSettings, SortingCriteria sortFlags, ref RendererListHandle rl) { // TODO: When importing project, AssetPreviewUpdater::CreatePreviewForAsset will be called multiple times. // This might be in a point that some resources required for the pipeline are not finished importing yet. // Proper fix is to add a fence on asset import. if (errorMaterial == null) { rl = new RendererListHandle(); return; } RendererListParams param = new RendererListParams(); CreateRendererParamsObjectsWithError(ref cullResults, camera, filterSettings, sortFlags, ref param); rl = renderGraph.CreateRendererList(param); } [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")] internal static void DrawRendererListObjectsWithError(RasterCommandBuffer cmd, ref RendererList rl) { cmd.DrawRendererList(rl); } // Create a RendererList using a RenderStateBlock override is quite common so we have this optimized utility function for it internal static void CreateRendererListWithRenderStateBlock(ScriptableRenderContext context, ref CullingResults cullResults, DrawingSettings ds, FilteringSettings fs, RenderStateBlock rsb, ref RendererList rl) { RendererListParams param = new RendererListParams(); unsafe { // Taking references to stack variables in the current function does not require any pinning (as long as you stay within the scope) // so we can safely alias it as a native array RenderStateBlock* rsbPtr = &rsb; var stateBlocks = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(rsbPtr, 1, Allocator.None); var shaderTag = ShaderTagId.none; var tagValues = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(&shaderTag, 1, Allocator.None); // Inside CreateRendererList (below), we pass the NativeArrays to C++ by calling GetUnsafeReadOnlyPtr // This will check read access but NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray does not set up the SafetyHandle (by design) so create/add it here // NOTE: we explicitly share the handle #if ENABLE_UNITY_COLLECTIONS_CHECKS var safetyHandle = AtomicSafetyHandle.Create(); AtomicSafetyHandle.SetAllowReadOrWriteAccess(safetyHandle, true); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref stateBlocks, safetyHandle); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref tagValues, safetyHandle); #endif // Create & schedule the RL param = new RendererListParams(cullResults, ds, fs) { tagValues = tagValues, stateBlocks = stateBlocks }; rl = context.CreateRendererList(ref param); // we need to explicitly release the SafetyHandle #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.Release(safetyHandle); #endif } } static ShaderTagId[] s_ShaderTagValues = new ShaderTagId[1]; static RenderStateBlock[] s_RenderStateBlocks = new RenderStateBlock[1]; // Create a RendererList using a RenderStateBlock override is quite common so we have this optimized utility function for it internal static void CreateRendererListWithRenderStateBlock(RenderGraph renderGraph, ref CullingResults cullResults, DrawingSettings ds, FilteringSettings fs, RenderStateBlock rsb, ref RendererListHandle rl) { s_ShaderTagValues[0] = ShaderTagId.none; s_RenderStateBlocks[0] = rsb; NativeArray tagValues = new NativeArray(s_ShaderTagValues, Allocator.Temp); NativeArray stateBlocks = new NativeArray(s_RenderStateBlocks, Allocator.Temp); var param = new RendererListParams(cullResults, ds, fs) { tagValues = tagValues, stateBlocks = stateBlocks, isPassTagName = false }; rl = renderGraph.CreateRendererList(param); } // Caches render texture format support. SystemInfo.SupportsRenderTextureFormat allocates memory due to boxing. static Dictionary m_RenderTextureFormatSupport = new Dictionary(); internal static void ClearSystemInfoCache() { m_RenderTextureFormatSupport.Clear(); } /// /// Checks if a render texture format is supported by the run-time system. /// Similar to , but doesn't allocate memory. /// /// The format to look up. /// Returns true if the graphics card supports the given RenderTextureFormat public static bool SupportsRenderTextureFormat(RenderTextureFormat format) { if (!m_RenderTextureFormatSupport.TryGetValue(format, out var support)) { support = SystemInfo.SupportsRenderTextureFormat(format); m_RenderTextureFormatSupport.Add(format, support); } return support; } /// /// Obsolete. Use instead. /// /// The format to look up. /// The format usage to look up. /// Returns true if the graphics card supports the given GraphicsFormat [Obsolete("Use SystemInfo.IsFormatSupported instead.", false)] public static bool SupportsGraphicsFormat(GraphicsFormat format, FormatUsage usage) { GraphicsFormatUsage graphicsFormatUsage = (GraphicsFormatUsage)(1 << (int)usage); return SystemInfo.IsFormatSupported(format, graphicsFormatUsage); } /// /// Return the last colorBuffer index actually referring to an existing RenderTarget /// /// /// internal static int GetLastValidColorBufferIndex(RenderTargetIdentifier[] colorBuffers) { int i = colorBuffers.Length - 1; for (; i >= 0; --i) { if (colorBuffers[i] != 0) break; } return i; } /// /// Return the number of items in colorBuffers actually referring to an existing RenderTarget /// /// /// internal static uint GetValidColorBufferCount(RTHandle[] colorBuffers) { uint nonNullColorBuffers = 0; if (colorBuffers != null) { foreach (var identifier in colorBuffers) { if (identifier != null && identifier.nameID != 0) ++nonNullColorBuffers; } } return nonNullColorBuffers; } /// /// Return true if colorBuffers is an actual MRT setup /// /// /// internal static bool IsMRT(RTHandle[] colorBuffers) { return GetValidColorBufferCount(colorBuffers) > 1; } /// /// Return true if value can be found in source (without recurring to Linq) /// /// /// /// internal static bool Contains(RenderTargetIdentifier[] source, RenderTargetIdentifier value) { foreach (var identifier in source) { if (identifier == value) return true; } return false; } /// /// Return the index where value was found source. Otherwise, return -1. (without recurring to Linq) /// /// /// /// internal static int IndexOf(RTHandle[] source, RenderTargetIdentifier value) { for (int i = 0; i < source.Length; ++i) { if (source[i] == value) return i; } return -1; } /// /// Return the index where value was found source. Otherwise, return -1. (without recurring to Linq) /// /// /// /// internal static int IndexOf(RTHandle[] source, RTHandle value) => IndexOf(source, value.nameID); /// /// Return the number of RenderTargetIdentifiers in "source" that are valid (not 0) and different from "value" (without recurring to Linq) /// /// /// /// internal static uint CountDistinct(RTHandle[] source, RTHandle value) { uint count = 0; for (int i = 0; i < source.Length; ++i) { if (source[i] != null && source[i].nameID != 0 && source[i].nameID != value.nameID) ++count; } return count; } /// /// Return the index of last valid (i.e different from 0) RenderTargetIdentifiers in "source" (without recurring to Linq) /// /// /// internal static int LastValid(RTHandle[] source) { for (int i = source.Length - 1; i >= 0; --i) { if (source[i] != null && source[i].nameID != 0) return i; } return -1; } /// /// Return true if ClearFlag a contains ClearFlag b /// /// /// /// internal static bool Contains(ClearFlag a, ClearFlag b) { return (a & b) == b; } /// /// Return true if "left" and "right" are the same (without recurring to Linq) /// /// /// /// internal static bool SequenceEqual(RTHandle[] left, RTHandle[] right) { if (left.Length != right.Length) return false; for (int i = 0; i < left.Length; ++i) if (left[i].nameID != right[i].nameID) return false; return true; } internal static bool MultisampleDepthResolveSupported() { // Temporarily disabling depth resolve a driver bug on OSX when using some AMD graphics cards. Temporarily disabling depth resolve on that platform // TODO: re-enable once the issue is investigated/fixed if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.OSXPlayer) return false; // Should we also check if the format has stencil and check stencil resolve capability only in that case? return SystemInfo.supportsMultisampleResolveDepth && SystemInfo.supportsMultisampleResolveStencil; } /// /// Return true if handle does not match descriptor /// /// RTHandle to check (can be null) /// Descriptor for the RTHandle to match /// Filtering mode of the RTHandle. /// Addressing mode of the RTHandle. /// Anisotropic filtering level. /// Bias applied to mipmaps during filtering. /// Name of the RTHandle. /// Check if the RTHandle has auto scaling enabled if not, check the widths and heights /// internal static bool RTHandleNeedsReAlloc( RTHandle handle, in TextureDesc descriptor, bool scaled) { if (handle == null || handle.rt == null) return true; if (handle.useScaling != scaled) return true; if (!scaled && (handle.rt.width != descriptor.width || handle.rt.height != descriptor.height)) return true; return (DepthBits)handle.rt.descriptor.depthBufferBits != descriptor.depthBufferBits || (handle.rt.descriptor.depthBufferBits == (int)DepthBits.None && handle.rt.descriptor.graphicsFormat != descriptor.colorFormat) || handle.rt.descriptor.dimension != descriptor.dimension || handle.rt.descriptor.enableRandomWrite != descriptor.enableRandomWrite || handle.rt.descriptor.useMipMap != descriptor.useMipMap || handle.rt.descriptor.autoGenerateMips != descriptor.autoGenerateMips || (MSAASamples)handle.rt.descriptor.msaaSamples != descriptor.msaaSamples || handle.rt.descriptor.bindMS != descriptor.bindTextureMS || handle.rt.descriptor.useDynamicScale != descriptor.useDynamicScale || handle.rt.descriptor.memoryless != descriptor.memoryless || handle.rt.filterMode != descriptor.filterMode || handle.rt.wrapMode != descriptor.wrapMode || handle.rt.anisoLevel != descriptor.anisoLevel || handle.rt.mipMapBias != descriptor.mipMapBias || handle.name != descriptor.name; } /// /// Returns the RenderTargetIdentifier of the current camera target. /// /// /// internal static RenderTargetIdentifier GetCameraTargetIdentifier(ref RenderingData renderingData) { // Note: We need to get the cameraData.targetTexture as this will get the targetTexture of the camera stack. // Overlay cameras need to output to the target described in the base camera while doing camera stack. ref CameraData cameraData = ref renderingData.cameraData; RenderTargetIdentifier cameraTarget = (cameraData.targetTexture != null) ? new RenderTargetIdentifier(cameraData.targetTexture) : BuiltinRenderTextureType.CameraTarget; #if ENABLE_VR && ENABLE_XR_MODULE if (cameraData.xr.enabled) { if (cameraData.xr.singlePassEnabled) { cameraTarget = cameraData.xr.renderTarget; } else { int depthSlice = cameraData.xr.GetTextureArraySlice(); cameraTarget = new RenderTargetIdentifier(cameraData.xr.renderTarget, 0, CubemapFace.Unknown, depthSlice); } } #endif return cameraTarget; } /// /// Re-allocate fixed-size RTHandle if it is not allocated or doesn't match the descriptor /// /// RTHandle to check (can be null) /// Descriptor for the RTHandle to match /// Filtering mode of the RTHandle. /// Addressing mode of the RTHandle. /// Set to true if the depth buffer should be used as a shadow map. /// Anisotropic filtering level. /// Bias applied to mipmaps during filtering. /// Name of the RTHandle. /// If an allocation was done. [Obsolete("This method will be removed in a future release. Please use ReAllocateHandleIfNeeded instead. #from(2023.3)")] public static bool ReAllocateIfNeeded( ref RTHandle handle, in RenderTextureDescriptor descriptor, FilterMode filterMode = FilterMode.Point, TextureWrapMode wrapMode = TextureWrapMode.Repeat, bool isShadowMap = false, int anisoLevel = 1, float mipMapBias = 0, string name = "") { TextureDesc requestRTDesc = RTHandleResourcePool.CreateTextureDesc(descriptor, TextureSizeMode.Explicit, anisoLevel, 0, filterMode, wrapMode, name); if (RTHandleNeedsReAlloc(handle, requestRTDesc, false)) { if (handle != null && handle.rt != null) { TextureDesc currentRTDesc = RTHandleResourcePool.CreateTextureDesc(handle.rt.descriptor, TextureSizeMode.Explicit, handle.rt.anisoLevel, handle.rt.mipMapBias, handle.rt.filterMode, handle.rt.wrapMode, handle.name); AddStaleResourceToPoolOrRelease(currentRTDesc, handle); } if (UniversalRenderPipeline.s_RTHandlePool.TryGetResource(requestRTDesc, out handle)) { return true; } else { handle = RTHandles.Alloc(descriptor, filterMode, wrapMode, isShadowMap, anisoLevel, mipMapBias, name); return true; } } return false; } /// /// Re-allocate dynamically resized RTHandle if it is not allocated or doesn't match the descriptor /// /// RTHandle to check (can be null) /// Constant scale for the RTHandle size computation. /// Descriptor for the RTHandle to match /// Filtering mode of the RTHandle. /// Addressing mode of the RTHandle. /// Set to true if the depth buffer should be used as a shadow map. /// Anisotropic filtering level. /// Bias applied to mipmaps during filtering. /// Name of the RTHandle. /// If the RTHandle should be re-allocated [Obsolete("This method will be removed in a future release. Please use ReAllocateHandleIfNeeded instead. #from(2023.3)")] public static bool ReAllocateIfNeeded( ref RTHandle handle, Vector2 scaleFactor, in RenderTextureDescriptor descriptor, FilterMode filterMode = FilterMode.Point, TextureWrapMode wrapMode = TextureWrapMode.Repeat, bool isShadowMap = false, int anisoLevel = 1, float mipMapBias = 0, string name = "") { var usingConstantScale = handle != null && handle.useScaling && handle.scaleFactor == scaleFactor; TextureDesc requestRTDesc = RTHandleResourcePool.CreateTextureDesc(descriptor, TextureSizeMode.Scale, anisoLevel, 0, filterMode, wrapMode); if (!usingConstantScale || RTHandleNeedsReAlloc(handle, requestRTDesc, true)) { if (handle != null && handle.rt != null) { TextureDesc currentRTDesc = RTHandleResourcePool.CreateTextureDesc(handle.rt.descriptor, TextureSizeMode.Scale, handle.rt.anisoLevel, handle.rt.mipMapBias, handle.rt.filterMode, handle.rt.wrapMode); AddStaleResourceToPoolOrRelease(currentRTDesc, handle); } if (UniversalRenderPipeline.s_RTHandlePool.TryGetResource(requestRTDesc, out handle)) { return true; } else { handle = RTHandles.Alloc(scaleFactor, descriptor, filterMode, wrapMode, isShadowMap, anisoLevel, mipMapBias, name); return true; } } return false; } /// /// Re-allocate dynamically resized RTHandle if it is not allocated or doesn't match the descriptor /// /// RTHandle to check (can be null) /// Function used for the RTHandle size computation. /// Descriptor for the RTHandle to match /// Filtering mode of the RTHandle. /// Addressing mode of the RTHandle. /// Set to true if the depth buffer should be used as a shadow map. /// Anisotropic filtering level. /// Bias applied to mipmaps during filtering. /// Name of the RTHandle. /// If an allocation was done [Obsolete("This method will be removed in a future release. Please use ReAllocateHandleIfNeeded instead. #from(2023.3)")] public static bool ReAllocateIfNeeded( ref RTHandle handle, ScaleFunc scaleFunc, in RenderTextureDescriptor descriptor, FilterMode filterMode = FilterMode.Point, TextureWrapMode wrapMode = TextureWrapMode.Repeat, bool isShadowMap = false, int anisoLevel = 1, float mipMapBias = 0, string name = "") { var usingScaleFunction = handle != null && handle.useScaling && handle.scaleFactor == Vector2.zero; TextureDesc requestRTDesc = RTHandleResourcePool.CreateTextureDesc(descriptor, TextureSizeMode.Functor, anisoLevel, 0, filterMode, wrapMode); if (!usingScaleFunction || RTHandleNeedsReAlloc(handle, requestRTDesc, true)) { if (handle != null && handle.rt != null) { TextureDesc currentRTDesc = RTHandleResourcePool.CreateTextureDesc(handle.rt.descriptor, TextureSizeMode.Functor, handle.rt.anisoLevel, handle.rt.mipMapBias, handle.rt.filterMode, handle.rt.wrapMode); AddStaleResourceToPoolOrRelease(currentRTDesc, handle); } if (UniversalRenderPipeline.s_RTHandlePool.TryGetResource(requestRTDesc, out handle)) { return true; } else { handle = RTHandles.Alloc(scaleFunc, descriptor, filterMode, wrapMode, isShadowMap, anisoLevel, mipMapBias, name); return true; } } return false; } /// /// Re-allocate fixed-size RTHandle if it is not allocated or doesn't match the descriptor /// /// RTHandle to check (can be null) /// Descriptor for the RTHandle to match /// Filtering mode of the RTHandle. /// Addressing mode of the RTHandle. /// Anisotropic filtering level. /// Bias applied to mipmaps during filtering. /// Name of the RTHandle. /// If an allocation was done. public static bool ReAllocateHandleIfNeeded( ref RTHandle handle, in RenderTextureDescriptor descriptor, FilterMode filterMode = FilterMode.Point, TextureWrapMode wrapMode = TextureWrapMode.Repeat, int anisoLevel = 1, float mipMapBias = 0, string name = "") { TextureDesc requestRTDesc = RTHandleResourcePool.CreateTextureDesc(descriptor, TextureSizeMode.Explicit, anisoLevel, 0, filterMode, wrapMode, name); if (RTHandleNeedsReAlloc(handle, requestRTDesc, false)) { if (handle != null && handle.rt != null) { TextureDesc currentRTDesc = RTHandleResourcePool.CreateTextureDesc(handle.rt.descriptor, TextureSizeMode.Explicit, handle.rt.anisoLevel, handle.rt.mipMapBias, handle.rt.filterMode, handle.rt.wrapMode, handle.name); AddStaleResourceToPoolOrRelease(currentRTDesc, handle); } if (UniversalRenderPipeline.s_RTHandlePool.TryGetResource(requestRTDesc, out handle)) { return true; } var actualFormat = descriptor.graphicsFormat != GraphicsFormat.None ? descriptor.graphicsFormat : descriptor.depthStencilFormat; RTHandleAllocInfo allocInfo = new RTHandleAllocInfo(); allocInfo.slices = descriptor.volumeDepth; allocInfo.format = actualFormat; allocInfo.filterMode = filterMode; allocInfo.wrapModeU = wrapMode; allocInfo.wrapModeV = wrapMode; allocInfo.wrapModeW = wrapMode; allocInfo.dimension = descriptor.dimension; allocInfo.enableRandomWrite = descriptor.enableRandomWrite; allocInfo.useMipMap = descriptor.useMipMap; allocInfo.autoGenerateMips = descriptor.autoGenerateMips; allocInfo.anisoLevel = anisoLevel; allocInfo.mipMapBias = mipMapBias; allocInfo.msaaSamples = (MSAASamples)descriptor.msaaSamples; allocInfo.bindTextureMS = descriptor.bindMS; allocInfo.useDynamicScale = descriptor.useDynamicScale; allocInfo.memoryless = descriptor.memoryless; allocInfo.vrUsage = descriptor.vrUsage; allocInfo.name = name; handle = RTHandles.Alloc(descriptor.width, descriptor.height, allocInfo); return true; } return false; } /// /// Re-allocate dynamically resized RTHandle if it is not allocated or doesn't match the descriptor /// /// RTHandle to check (can be null) /// Constant scale for the RTHandle size computation. /// Descriptor for the RTHandle to match /// Filtering mode of the RTHandle. /// Addressing mode of the RTHandle. /// Anisotropic filtering level. /// Bias applied to mipmaps during filtering. /// Name of the RTHandle. /// If an allocation was done. public static bool ReAllocateHandleIfNeeded( ref RTHandle handle, Vector2 scaleFactor, in RenderTextureDescriptor descriptor, FilterMode filterMode = FilterMode.Point, TextureWrapMode wrapMode = TextureWrapMode.Repeat, int anisoLevel = 1, float mipMapBias = 0, string name = "") { var usingConstantScale = handle != null && handle.useScaling && handle.scaleFactor == scaleFactor; TextureDesc requestRTDesc = RTHandleResourcePool.CreateTextureDesc(descriptor, TextureSizeMode.Scale, anisoLevel, 0, filterMode, wrapMode); if (!usingConstantScale || RTHandleNeedsReAlloc(handle, requestRTDesc, true)) { if (handle != null && handle.rt != null) { TextureDesc currentRTDesc = RTHandleResourcePool.CreateTextureDesc(handle.rt.descriptor, TextureSizeMode.Scale, handle.rt.anisoLevel, handle.rt.mipMapBias, handle.rt.filterMode, handle.rt.wrapMode); AddStaleResourceToPoolOrRelease(currentRTDesc, handle); } if (UniversalRenderPipeline.s_RTHandlePool.TryGetResource(requestRTDesc, out handle)) { return true; } var actualFormat = descriptor.graphicsFormat != GraphicsFormat.None ? descriptor.graphicsFormat : descriptor.depthStencilFormat; RTHandleAllocInfo allocInfo = new RTHandleAllocInfo(); allocInfo.slices = descriptor.volumeDepth; allocInfo.format = actualFormat; allocInfo.filterMode = filterMode; allocInfo.wrapModeU = wrapMode; allocInfo.wrapModeV = wrapMode; allocInfo.wrapModeW = wrapMode; allocInfo.dimension = descriptor.dimension; allocInfo.enableRandomWrite = descriptor.enableRandomWrite; allocInfo.useMipMap = descriptor.useMipMap; allocInfo.autoGenerateMips = descriptor.autoGenerateMips; allocInfo.anisoLevel = anisoLevel; allocInfo.mipMapBias = mipMapBias; allocInfo.msaaSamples = (MSAASamples)descriptor.msaaSamples; allocInfo.bindTextureMS = descriptor.bindMS; allocInfo.useDynamicScale = descriptor.useDynamicScale; allocInfo.memoryless = descriptor.memoryless; allocInfo.vrUsage = descriptor.vrUsage; allocInfo.name = name; handle = RTHandles.Alloc(scaleFactor, allocInfo); return true; } return false; } /// /// Re-allocate dynamically resized RTHandle if it is not allocated or doesn't match the descriptor /// /// RTHandle to check (can be null) /// Function used for the RTHandle size computation. /// Descriptor for the RTHandle to match /// Filtering mode of the RTHandle. /// Addressing mode of the RTHandle. /// Anisotropic filtering level. /// Bias applied to mipmaps during filtering. /// Name of the RTHandle. /// If an allocation was done. public static bool ReAllocateHandleIfNeeded( ref RTHandle handle, ScaleFunc scaleFunc, in RenderTextureDescriptor descriptor, FilterMode filterMode = FilterMode.Point, TextureWrapMode wrapMode = TextureWrapMode.Repeat, int anisoLevel = 1, float mipMapBias = 0, string name = "") { var usingScaleFunction = handle != null && handle.useScaling && handle.scaleFactor == Vector2.zero; TextureDesc requestRTDesc = RTHandleResourcePool.CreateTextureDesc(descriptor, TextureSizeMode.Functor, anisoLevel, 0, filterMode, wrapMode); if (!usingScaleFunction || RTHandleNeedsReAlloc(handle, requestRTDesc, true)) { if (handle != null && handle.rt != null) { TextureDesc currentRTDesc = RTHandleResourcePool.CreateTextureDesc(handle.rt.descriptor, TextureSizeMode.Functor, handle.rt.anisoLevel, handle.rt.mipMapBias, handle.rt.filterMode, handle.rt.wrapMode); AddStaleResourceToPoolOrRelease(currentRTDesc, handle); } if (UniversalRenderPipeline.s_RTHandlePool.TryGetResource(requestRTDesc, out handle)) { return true; } var actualFormat = descriptor.graphicsFormat != GraphicsFormat.None ? descriptor.graphicsFormat : descriptor.depthStencilFormat; RTHandleAllocInfo allocInfo = new RTHandleAllocInfo(); allocInfo.slices = descriptor.volumeDepth; allocInfo.format = actualFormat; allocInfo.filterMode = filterMode; allocInfo.wrapModeU = wrapMode; allocInfo.wrapModeV = wrapMode; allocInfo.wrapModeW = wrapMode; allocInfo.dimension = descriptor.dimension; allocInfo.enableRandomWrite = descriptor.enableRandomWrite; allocInfo.useMipMap = descriptor.useMipMap; allocInfo.autoGenerateMips = descriptor.autoGenerateMips; allocInfo.anisoLevel = anisoLevel; allocInfo.mipMapBias = mipMapBias; allocInfo.msaaSamples = (MSAASamples)descriptor.msaaSamples; allocInfo.bindTextureMS = descriptor.bindMS; allocInfo.useDynamicScale = descriptor.useDynamicScale; allocInfo.memoryless = descriptor.memoryless; allocInfo.vrUsage = descriptor.vrUsage; allocInfo.name = name; handle = RTHandles.Alloc(scaleFunc, allocInfo); return true; } return false; } /// /// Resize the rthandle pool's max stale resource capacity. The default value is 32. /// Increasing the capacity may have a negative impact on the memory usage(dued to staled resources in pool). /// Increasing the capacity may improve runtime performance (by reducing the runtime RTHandle realloc count in multi view/multi camera setup). /// Setting capacity will purge the current pool. It is recommended to setup the capacity upfront and not changing it during the runtime. /// /// Max capacity to set /// Return true if set successfully. Return false if URP is not initialized and pool does not exist yet. public static bool SetMaxRTHandlePoolCapacity(int capacity) { if (UniversalRenderPipeline.s_RTHandlePool == null) return false; UniversalRenderPipeline.s_RTHandlePool.staleResourceCapacity = capacity; return true; } /// /// Add stale rtHandle to pool so that it could be reused in the future. /// For stale rtHandle failed to add to pool(could happen when pool is reaching its max stale resource capacity), the stale resource will be released. /// internal static void AddStaleResourceToPoolOrRelease(TextureDesc desc, RTHandle handle) { if (!UniversalRenderPipeline.s_RTHandlePool.AddResourceToPool(desc, handle, Time.frameCount)) RTHandles.Release(handle); } /// /// Creates DrawingSettings based on current the rendering state. /// /// Shader pass tag to render. /// Current rendering state. /// Criteria to sort objects being rendered. /// /// static public DrawingSettings CreateDrawingSettings(ShaderTagId shaderTagId, ref RenderingData renderingData, SortingCriteria sortingCriteria) { UniversalRenderingData universalRenderingData = renderingData.frameData.Get(); UniversalCameraData cameraData = renderingData.frameData.Get(); UniversalLightData lightData = renderingData.frameData.Get(); return CreateDrawingSettings(shaderTagId, universalRenderingData, cameraData, lightData, sortingCriteria); } /// /// Creates DrawingSettings based on current the rendering state. /// /// Shader pass tag to render. /// Current rendering state. /// Current camera state. /// Current light state. /// Criteria to sort objects being rendered. /// /// static public DrawingSettings CreateDrawingSettings(ShaderTagId shaderTagId, UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData, SortingCriteria sortingCriteria) { Camera camera = cameraData.camera; SortingSettings sortingSettings = new SortingSettings(camera) { criteria = sortingCriteria }; DrawingSettings settings = new DrawingSettings(shaderTagId, sortingSettings) { perObjectData = renderingData.perObjectData, mainLightIndex = lightData.mainLightIndex, enableDynamicBatching = renderingData.supportsDynamicBatching, // Disable instancing for preview cameras. This is consistent with the built-in forward renderer. Also fixes case 1127324. enableInstancing = camera.cameraType == CameraType.Preview ? false : true, }; return settings; } /// /// Creates DrawingSettings based on current rendering state. /// /// List of shader pass tag to render. /// Current rendering state. /// Criteria to sort objects being rendered. /// /// static public DrawingSettings CreateDrawingSettings(List shaderTagIdList, ref RenderingData renderingData, SortingCriteria sortingCriteria) { UniversalRenderingData universalRenderingData = renderingData.frameData.Get(); UniversalCameraData cameraData = renderingData.frameData.Get(); UniversalLightData lightData = renderingData.frameData.Get(); return CreateDrawingSettings(shaderTagIdList, universalRenderingData, cameraData, lightData, sortingCriteria); } /// /// Creates DrawingSettings based on current rendering state. /// /// List of shader pass tag to render. /// Current rendering state. /// Current camera state. /// Current light state. /// Criteria to sort objects being rendered. /// /// static public DrawingSettings CreateDrawingSettings(List shaderTagIdList, UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData, SortingCriteria sortingCriteria) { if (shaderTagIdList == null || shaderTagIdList.Count == 0) { Debug.LogWarning("ShaderTagId list is invalid. DrawingSettings is created with default pipeline ShaderTagId"); return CreateDrawingSettings(new ShaderTagId("UniversalPipeline"), renderingData, cameraData, lightData, sortingCriteria); } DrawingSettings settings = CreateDrawingSettings(shaderTagIdList[0], renderingData, cameraData, lightData, sortingCriteria); for (int i = 1; i < shaderTagIdList.Count; ++i) settings.SetShaderPassName(i, shaderTagIdList[i]); return settings; } /// /// Returns the scale bias vector to use for final blits to the backbuffer, based on scaling mode and y-flip platform requirements. /// /// /// /// /// internal static Vector4 GetFinalBlitScaleBias(RTHandle source, RTHandle destination, UniversalCameraData cameraData) { Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one; var yflip = cameraData.IsRenderTargetProjectionMatrixFlipped(destination); Vector4 scaleBias = !yflip ? new Vector4(viewportScale.x, -viewportScale.y, 0, viewportScale.y) : new Vector4(viewportScale.x, viewportScale.y, 0, 0); return scaleBias; } } }