using System.Collections.Generic; using System.Diagnostics; using UnityEngine.Experimental.Rendering; 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. /// 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) { // GLES2 does not support bitwise operations. return type != GraphicsDeviceType.OpenGLES2; } 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) { 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); } } #if ENABLE_VR && ENABLE_XR_MODULE internal static readonly int UNITY_STEREO_MATRIX_V = Shader.PropertyToID("unity_StereoMatrixV"); internal static readonly int UNITY_STEREO_MATRIX_IV = Shader.PropertyToID("unity_StereoMatrixInvV"); internal static readonly int UNITY_STEREO_MATRIX_P = Shader.PropertyToID("unity_StereoMatrixP"); internal static readonly int UNITY_STEREO_MATRIX_IP = Shader.PropertyToID("unity_StereoMatrixInvP"); internal static readonly int UNITY_STEREO_MATRIX_VP = Shader.PropertyToID("unity_StereoMatrixVP"); internal static readonly int UNITY_STEREO_MATRIX_IVP = Shader.PropertyToID("unity_StereoMatrixInvVP"); internal static readonly int UNITY_STEREO_CAMERA_PROJECTION = Shader.PropertyToID("unity_StereoCameraProjection"); internal static readonly int UNITY_STEREO_CAMERA_INV_PROJECTION = Shader.PropertyToID("unity_StereoCameraInvProjection"); internal static readonly int UNITY_STEREO_VECTOR_CAMPOS = Shader.PropertyToID("unity_StereoWorldSpaceCameraPos"); // Hold the stereo matrices in this class to avoid allocating arrays every frame internal class StereoConstants { public Matrix4x4[] viewProjMatrix = new Matrix4x4[2]; public Matrix4x4[] invViewMatrix = new Matrix4x4[2]; public Matrix4x4[] invProjMatrix = new Matrix4x4[2]; public Matrix4x4[] invViewProjMatrix = new Matrix4x4[2]; public Matrix4x4[] invCameraProjMatrix = new Matrix4x4[2]; public Vector4[] worldSpaceCameraPos = new Vector4[2]; }; static readonly StereoConstants stereoConstants = new StereoConstants(); /// /// Helper function to set all view and projection related matrices /// Should be called before draw call and after cmd.SetRenderTarget /// Internal usage only, function name and signature may be subject to change /// /// CommandBuffer to submit data to GPU. /// View matrix to be set. Array size is 2. /// Projection matrix to be set.Array size is 2. /// Camera projection matrix to be set.Array size is 2. Does not include platform specific transformations such as depth-reverse, depth range in post-projective space and y-flip. /// Set this to true if you also need to set inverse camera matrices. /// Void internal static void SetStereoViewAndProjectionMatrices(CommandBuffer cmd, Matrix4x4[] viewMatrix, Matrix4x4[] projMatrix, Matrix4x4[] cameraProjMatrix, bool setInverseMatrices) { for (int i = 0; i < 2; i++) { stereoConstants.viewProjMatrix[i] = projMatrix[i] * viewMatrix[i]; stereoConstants.invViewMatrix[i] = Matrix4x4.Inverse(viewMatrix[i]); stereoConstants.invProjMatrix[i] = Matrix4x4.Inverse(projMatrix[i]); stereoConstants.invViewProjMatrix[i] = Matrix4x4.Inverse(stereoConstants.viewProjMatrix[i]); stereoConstants.invCameraProjMatrix[i] = Matrix4x4.Inverse(cameraProjMatrix[i]); stereoConstants.worldSpaceCameraPos[i] = stereoConstants.invViewMatrix[i].GetColumn(3); } cmd.SetGlobalMatrixArray(UNITY_STEREO_MATRIX_V, viewMatrix); cmd.SetGlobalMatrixArray(UNITY_STEREO_MATRIX_P, projMatrix); cmd.SetGlobalMatrixArray(UNITY_STEREO_MATRIX_VP, stereoConstants.viewProjMatrix); cmd.SetGlobalMatrixArray(UNITY_STEREO_CAMERA_PROJECTION, cameraProjMatrix); if (setInverseMatrices) { cmd.SetGlobalMatrixArray(UNITY_STEREO_MATRIX_IV, stereoConstants.invViewMatrix); cmd.SetGlobalMatrixArray(UNITY_STEREO_MATRIX_IP, stereoConstants.invProjMatrix); cmd.SetGlobalMatrixArray(UNITY_STEREO_MATRIX_IVP, stereoConstants.invViewProjMatrix); cmd.SetGlobalMatrixArray(UNITY_STEREO_CAMERA_INV_PROJECTION, stereoConstants.invCameraProjMatrix); } cmd.SetGlobalVectorArray(UNITY_STEREO_VECTOR_CAMPOS, stereoConstants.worldSpaceCameraPos); } #endif internal static void SetScaleBiasRt(CommandBuffer 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; bool isCameraColorFinalTarget = (cameraData.cameraType == CameraType.Game && renderer.cameraColorTarget == 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 Blit(CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material, int passIndex = 0, bool useDrawProcedural = false, RenderBufferLoadAction colorLoadAction = RenderBufferLoadAction.Load, RenderBufferStoreAction colorStoreAction = RenderBufferStoreAction.Store, RenderBufferLoadAction depthLoadAction = RenderBufferLoadAction.Load, RenderBufferStoreAction depthStoreAction = RenderBufferStoreAction.Store) { cmd.SetGlobalTexture(ShaderPropertyId.sourceTex, source); if (useDrawProcedural) { Vector4 scaleBias = new Vector4(1, 1, 0, 0); Vector4 scaleBiasRt = new Vector4(1, 1, 0, 0); cmd.SetGlobalVector(ShaderPropertyId.scaleBias, scaleBias); cmd.SetGlobalVector(ShaderPropertyId.scaleBiasRt, scaleBiasRt); cmd.SetRenderTarget(new RenderTargetIdentifier(destination, 0, CubemapFace.Unknown, -1), colorLoadAction, colorStoreAction, depthLoadAction, depthStoreAction); cmd.DrawProcedural(Matrix4x4.identity, material, passIndex, MeshTopology.Quads, 4, 1, null); } else { cmd.SetRenderTarget(destination, colorLoadAction, colorStoreAction, depthLoadAction, depthStoreAction); cmd.Blit(source, BuiltinRenderTextureType.CurrentActive, 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 RenderObjectsWithError(ScriptableRenderContext context, ref CullingResults cullResults, Camera camera, FilteringSettings filterSettings, SortingCriteria sortFlags) { // 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) return; 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]); context.DrawRenderers(cullResults, ref errorSettings, ref filterSettings); } // Caches render texture format support. SystemInfo.SupportsRenderTextureFormat and IsFormatSupported allocate memory due to boxing. static Dictionary m_RenderTextureFormatSupport = new Dictionary(); static Dictionary> m_GraphicsFormatSupport = new Dictionary>(); internal static void ClearSystemInfoCache() { m_RenderTextureFormatSupport.Clear(); m_GraphicsFormatSupport.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; } /// /// Checks if a texture format is supported by the run-time system. /// Similar to , but doesn't allocate memory. /// /// The format to look up. /// The format usage to look up. /// Returns true if the graphics card supports the given GraphicsFormat public static bool SupportsGraphicsFormat(GraphicsFormat format, FormatUsage usage) { bool support = false; if (!m_GraphicsFormatSupport.TryGetValue(format, out var uses)) { uses = new Dictionary(); support = SystemInfo.IsFormatSupported(format, usage); uses.Add(usage, support); m_GraphicsFormatSupport.Add(format, uses); } else { if (!uses.TryGetValue(usage, out support)) { support = SystemInfo.IsFormatSupported(format, usage); uses.Add(usage, support); } } return support; } /// /// 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(RenderTargetIdentifier[] colorBuffers) { uint nonNullColorBuffers = 0; if (colorBuffers != null) { foreach (var identifier in colorBuffers) { if (identifier != 0) ++nonNullColorBuffers; } } return nonNullColorBuffers; } /// /// Return true if colorBuffers is an actual MRT setup /// /// /// internal static bool IsMRT(RenderTargetIdentifier[] 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(RenderTargetIdentifier[] source, RenderTargetIdentifier value) { for (int i = 0; i < source.Length; ++i) { if (source[i] == value) return i; } return -1; } /// /// Return the number of RenderTargetIdentifiers in "source" that are valid (not 0) and different from "value" (without recurring to Linq) /// /// /// /// internal static uint CountDistinct(RenderTargetIdentifier[] source, RenderTargetIdentifier value) { uint count = 0; for (int i = 0; i < source.Length; ++i) { if (source[i] != value && source[i] != 0) ++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(RenderTargetIdentifier[] source) { for (int i = source.Length - 1; i >= 0; --i) { if (source[i] != 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(RenderTargetIdentifier[] left, RenderTargetIdentifier[] right) { if (left.Length != right.Length) return false; for (int i = 0; i < left.Length; ++i) if (left[i] != right[i]) return false; return true; } } }