using System; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.Universal.Internal; namespace UnityEngine.Rendering.Universal { internal sealed partial class Renderer2D : ScriptableRenderer { #if UNITY_SWITCH || UNITY_EMBEDDED_LINUX || UNITY_QNX const GraphicsFormat k_DepthStencilFormat = GraphicsFormat.D24_UNorm_S8_UInt; internal const int k_DepthBufferBits = 24; #else const GraphicsFormat k_DepthStencilFormat = GraphicsFormat.D32_SFloat_S8_UInt; internal const int k_DepthBufferBits = 32; #endif const int k_FinalBlitPassQueueOffset = 1; const int k_AfterFinalBlitPassQueueOffset = k_FinalBlitPassQueueOffset + 1; Render2DLightingPass m_Render2DLightingPass; PixelPerfectBackgroundPass m_PixelPerfectBackgroundPass; UpscalePass m_UpscalePass; CopyDepthPass m_CopyDepthPass; CopyCameraSortingLayerPass m_CopyCameraSortingLayerPass; FinalBlitPass m_FinalBlitPass; DrawScreenSpaceUIPass m_DrawOffscreenUIPass; DrawScreenSpaceUIPass m_DrawOverlayUIPass; // from HDRP code internal RenderTargetBufferSystem m_ColorBufferSystem; private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler("Create Camera Textures"); bool m_UseDepthStencilBuffer = true; bool m_CreateColorTexture; bool m_CreateDepthTexture; // We probably should declare these names in the base class, // as they must be the same across all ScriptableRenderer types for camera stacking to work. internal RTHandle m_ColorTextureHandle; internal RTHandle m_DepthTextureHandle; #if UNITY_EDITOR internal RTHandle m_DefaultWhiteTextureHandle; #endif Material m_BlitMaterial; Material m_BlitHDRMaterial; Material m_SamplingMaterial; Renderer2DData m_Renderer2DData; internal bool createColorTexture => m_CreateColorTexture; internal bool createDepthTexture => m_CreateDepthTexture; PostProcessPasses m_PostProcessPasses; internal ColorGradingLutPass colorGradingLutPass { get => m_PostProcessPasses.colorGradingLutPass; } internal PostProcessPass postProcessPass { get => m_PostProcessPasses.postProcessPass; } internal PostProcessPass finalPostProcessPass { get => m_PostProcessPasses.finalPostProcessPass; } internal RTHandle afterPostProcessColorHandle { get => m_PostProcessPasses.afterPostProcessColor; } internal RTHandle colorGradingLutHandle { get => m_PostProcessPasses.colorGradingLut; } /// public override int SupportedCameraStackingTypes() { return 1 << (int)CameraRenderType.Base | 1 << (int)CameraRenderType.Overlay; } public Renderer2D(Renderer2DData data) : base(data) { if (GraphicsSettings.TryGetRenderPipelineSettings( out var shadersResources)) { m_BlitMaterial = CoreUtils.CreateEngineMaterial(shadersResources.coreBlitPS); m_BlitHDRMaterial = CoreUtils.CreateEngineMaterial(shadersResources.blitHDROverlay); m_SamplingMaterial = CoreUtils.CreateEngineMaterial(shadersResources.samplingPS); } if (GraphicsSettings.TryGetRenderPipelineSettings(out var renderer2DResources)) { m_Render2DLightingPass = new Render2DLightingPass(data, m_BlitMaterial, m_SamplingMaterial, renderer2DResources.fallOffLookup); m_CopyDepthPass = new CopyDepthPass( RenderPassEvent.AfterRenderingTransparents, renderer2DResources.copyDepthPS, shouldClear: true, copyResolvedDepth: RenderingUtils.MultisampleDepthResolveSupported()); } // we should determine why clearing the camera target is set so late in the events... sounds like it could be earlier m_PixelPerfectBackgroundPass = new PixelPerfectBackgroundPass(RenderPassEvent.AfterRenderingTransparents); m_UpscalePass = new UpscalePass(RenderPassEvent.AfterRenderingPostProcessing, m_BlitMaterial); m_CopyCameraSortingLayerPass = new CopyCameraSortingLayerPass(m_BlitMaterial); m_FinalBlitPass = new FinalBlitPass(RenderPassEvent.AfterRendering + k_FinalBlitPassQueueOffset, m_BlitMaterial, m_BlitHDRMaterial); m_DrawOffscreenUIPass = new DrawScreenSpaceUIPass(RenderPassEvent.BeforeRenderingPostProcessing, true); m_DrawOverlayUIPass = new DrawScreenSpaceUIPass(RenderPassEvent.AfterRendering + k_AfterFinalBlitPassQueueOffset, false); // after m_FinalBlitPass // RenderTexture format depends on camera and pipeline (HDR, non HDR, etc) // Samples (MSAA) depend on camera and pipeline m_ColorBufferSystem = new RenderTargetBufferSystem("_CameraColorAttachment"); var ppParams = PostProcessParams.Create(); ppParams.blitMaterial = m_BlitMaterial; ppParams.requestColorFormat = GraphicsFormat.B10G11R11_UFloatPack32; m_PostProcessPasses = new PostProcessPasses(data.postProcessData, ref ppParams); m_UseDepthStencilBuffer = data.useDepthStencilBuffer; m_Renderer2DData = data; supportedRenderingFeatures = new RenderingFeatures(); m_Renderer2DData.lightCullResult = new Light2DCullResult(); LensFlareCommonSRP.mergeNeeded = 0; LensFlareCommonSRP.maxLensFlareWithOcclusionTemporalSample = 1; LensFlareCommonSRP.Initialize(); } protected override void Dispose(bool disposing) { m_Renderer2DData.Dispose(); m_Render2DLightingPass?.Dispose(); m_PostProcessPasses.Dispose(); m_ColorTextureHandle?.Release(); m_DepthTextureHandle?.Release(); ReleaseRenderTargets(); m_UpscalePass.Dispose(); m_CopyDepthPass?.Dispose(); m_FinalBlitPass?.Dispose(); m_DrawOffscreenUIPass?.Dispose(); m_DrawOverlayUIPass?.Dispose(); CoreUtils.Destroy(m_BlitMaterial); CoreUtils.Destroy(m_BlitHDRMaterial); CoreUtils.Destroy(m_SamplingMaterial); CleanupRenderGraphResources(); base.Dispose(disposing); } internal override void ReleaseRenderTargets() { m_ColorBufferSystem.Dispose(); m_PostProcessPasses.ReleaseRenderTargets(); } public Renderer2DData GetRenderer2DData() { return m_Renderer2DData; } private struct RenderPassInputSummary { internal bool requiresDepthTexture; internal bool requiresColorTexture; } private RenderPassInputSummary GetRenderPassInputs(UniversalCameraData cameraData) { RenderPassInputSummary inputSummary = new RenderPassInputSummary(); for (int i = 0; i < activeRenderPassQueue.Count; ++i) { ScriptableRenderPass pass = activeRenderPassQueue[i]; bool needsDepth = (pass.input & ScriptableRenderPassInput.Depth) != ScriptableRenderPassInput.None; bool needsColor = (pass.input & ScriptableRenderPassInput.Color) != ScriptableRenderPassInput.None; inputSummary.requiresDepthTexture |= needsDepth; inputSummary.requiresColorTexture |= needsColor; } inputSummary.requiresColorTexture |= cameraData.postProcessEnabled || cameraData.isHdrEnabled || cameraData.isSceneViewCamera || !cameraData.isDefaultViewport || cameraData.requireSrgbConversion || !cameraData.resolveFinalTarget || m_Renderer2DData.useCameraSortingLayerTexture || !Mathf.Approximately(cameraData.renderScale, 1.0f) || (DebugHandler != null && DebugHandler.WriteToDebugScreenTexture(cameraData.resolveFinalTarget)); inputSummary.requiresDepthTexture |= (!cameraData.resolveFinalTarget && m_UseDepthStencilBuffer); return inputSummary; } void CreateRenderTextures( ref RenderPassInputSummary renderPassInputs, CommandBuffer cmd, UniversalCameraData cameraData, bool forceCreateColorTexture, FilterMode colorTextureFilterMode, out RTHandle colorTargetHandle, out RTHandle depthTargetHandle) { ref var cameraTargetDescriptor = ref cameraData.cameraTargetDescriptor; var colorDescriptor = cameraTargetDescriptor; colorDescriptor.depthBufferBits = (int)DepthBits.None; m_ColorBufferSystem.SetCameraSettings(colorDescriptor, colorTextureFilterMode); if (cameraData.renderType == CameraRenderType.Base) { m_CreateColorTexture = renderPassInputs.requiresColorTexture; m_CreateDepthTexture = renderPassInputs.requiresDepthTexture; m_CreateColorTexture |= forceCreateColorTexture; // RTHandles do not support combining color and depth in the same texture so we create them separately m_CreateDepthTexture |= createColorTexture; if (createColorTexture) { if (m_ColorBufferSystem.PeekBackBuffer() == null || m_ColorBufferSystem.PeekBackBuffer().nameID != BuiltinRenderTextureType.CameraTarget) { m_ColorTextureHandle = m_ColorBufferSystem.GetBackBuffer(cmd); cmd.SetGlobalTexture("_CameraColorTexture", m_ColorTextureHandle.nameID); //Set _AfterPostProcessTexture, users might still rely on this although it is now always the cameratarget due to swapbuffer cmd.SetGlobalTexture("_AfterPostProcessTexture", m_ColorTextureHandle.nameID); } m_ColorTextureHandle = m_ColorBufferSystem.PeekBackBuffer(); } if (createDepthTexture) { var depthDescriptor = cameraTargetDescriptor; depthDescriptor.colorFormat = RenderTextureFormat.Depth; depthDescriptor.depthBufferBits = k_DepthBufferBits; if (!cameraData.resolveFinalTarget && m_UseDepthStencilBuffer) depthDescriptor.bindMS = depthDescriptor.msaaSamples > 1 && !SystemInfo.supportsMultisampleAutoResolve && (SystemInfo.supportsMultisampledTextures != 0); RenderingUtils.ReAllocateHandleIfNeeded(ref m_DepthTextureHandle, depthDescriptor, FilterMode.Point, wrapMode: TextureWrapMode.Clamp, name: "_CameraDepthAttachment"); } colorTargetHandle = createColorTexture ? m_ColorTextureHandle : k_CameraTarget; depthTargetHandle = createDepthTexture ? m_DepthTextureHandle : k_CameraTarget; } else // Overlay camera { cameraData.baseCamera.TryGetComponent(out var baseCameraData); var baseRenderer = (Renderer2D)baseCameraData.scriptableRenderer; if (m_ColorBufferSystem != baseRenderer.m_ColorBufferSystem) { m_ColorBufferSystem.Dispose(); m_ColorBufferSystem = baseRenderer.m_ColorBufferSystem; } // These render textures are created by the base camera, but it's the responsibility of the last overlay camera's ScriptableRenderer // to release the textures in its FinishRendering(). m_CreateColorTexture = true; m_CreateDepthTexture = true; m_ColorTextureHandle = baseRenderer.m_ColorTextureHandle; m_DepthTextureHandle = baseRenderer.m_DepthTextureHandle; colorTargetHandle = m_ColorTextureHandle; depthTargetHandle = m_DepthTextureHandle; } } [Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)] public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData) { UniversalRenderingData universalRenderingData = frameData.Get(); UniversalCameraData cameraData = frameData.Get(); UniversalPostProcessingData postProcessingData = frameData.Get(); ref var cameraTargetDescriptor = ref cameraData.cameraTargetDescriptor; bool stackHasPostProcess = postProcessingData.isEnabled && m_PostProcessPasses.isCreated; bool hasPostProcess = cameraData.postProcessEnabled && m_PostProcessPasses.isCreated; bool lastCameraInStack = cameraData.resolveFinalTarget; var colorTextureFilterMode = FilterMode.Bilinear; PixelPerfectCamera ppc = null; bool ppcUsesOffscreenRT = false; bool ppcUpscaleRT = false; if (DebugHandler != null) { #if UNITY_EDITOR UnityEditorInternal.SpriteMaskUtility.EnableDebugMode(DebugHandler.DebugDisplaySettings.materialSettings.materialDebugMode == DebugMaterialMode.SpriteMask); #endif if (DebugHandler.AreAnySettingsActive) { stackHasPostProcess = stackHasPostProcess && DebugHandler.IsPostProcessingAllowed; hasPostProcess = hasPostProcess && DebugHandler.IsPostProcessingAllowed; } DebugHandler.Setup(universalRenderingData.commandBuffer, cameraData.isPreviewCamera); if (DebugHandler.IsActiveForCamera(cameraData.isPreviewCamera)) { if (DebugHandler.WriteToDebugScreenTexture(cameraData.resolveFinalTarget)) { RenderTextureDescriptor descriptor = cameraData.cameraTargetDescriptor; DebugHandler.ConfigureColorDescriptorForDebugScreen(ref descriptor, cameraData.pixelWidth, cameraData.pixelHeight); RenderingUtils.ReAllocateHandleIfNeeded(ref DebugHandler.DebugScreenColorHandle, descriptor, name: "_DebugScreenColor"); RenderTextureDescriptor depthDesc = cameraData.cameraTargetDescriptor; DebugHandler.ConfigureDepthDescriptorForDebugScreen(ref depthDesc, k_DepthStencilFormat, cameraData.pixelWidth, cameraData.pixelHeight); RenderingUtils.ReAllocateHandleIfNeeded(ref DebugHandler.DebugScreenDepthHandle, depthDesc, name: "_DebugScreenDepth"); } if (DebugHandler.HDRDebugViewIsActive(cameraData.resolveFinalTarget)) { DebugHandler.hdrDebugViewPass.Setup(cameraData, DebugHandler.DebugDisplaySettings.lightingSettings.hdrDebugMode); EnqueuePass(DebugHandler.hdrDebugViewPass); } } } #if UNITY_EDITOR // The scene view camera cannot be uninitialized or skybox when using the 2D renderer. if (cameraData.cameraType == CameraType.SceneView) { cameraData.camera.clearFlags = CameraClearFlags.SolidColor; } #endif // Pixel Perfect Camera doesn't support camera stacking. if (cameraData.renderType == CameraRenderType.Base && lastCameraInStack) { cameraData.camera.TryGetComponent(out ppc); if (ppc != null && ppc.enabled) { if (ppc.offscreenRTSize != Vector2Int.zero) { ppcUsesOffscreenRT = true; // Pixel Perfect Camera may request a different RT size than camera VP size. // In that case we need to modify cameraTargetDescriptor here so that all the passes would use the same size. cameraTargetDescriptor.width = ppc.offscreenRTSize.x; cameraTargetDescriptor.height = ppc.offscreenRTSize.y; // If using FullScreenRenderPass with Pixel Perfect, we need to reallocate the size of the RT used var fullScreenRenderPass = activeRenderPassQueue.Find(x => x is FullScreenPassRendererFeature.FullScreenRenderPass) as FullScreenPassRendererFeature.FullScreenRenderPass; fullScreenRenderPass?.ReAllocate(cameraTargetDescriptor); } colorTextureFilterMode = FilterMode.Point; ppcUpscaleRT = ppc.gridSnapping == PixelPerfectCamera.GridSnapping.UpscaleRenderTexture || ppc.requiresUpscalePass; } } RenderPassInputSummary renderPassInputs = GetRenderPassInputs(cameraData); RTHandle colorTargetHandle; RTHandle depthTargetHandle; var cmd = universalRenderingData.commandBuffer; #if UNITY_EDITOR if(m_DefaultWhiteTextureHandle == null) m_DefaultWhiteTextureHandle = RTHandles.Alloc(Texture2D.whiteTexture, "_DefaultWhiteTex"); cmd.SetGlobalTexture(m_DefaultWhiteTextureHandle.name, m_DefaultWhiteTextureHandle.nameID); #endif using (new ProfilingScope(cmd, m_ProfilingSampler)) { CreateRenderTextures(ref renderPassInputs, cmd, cameraData, ppcUsesOffscreenRT, colorTextureFilterMode, out colorTargetHandle, out depthTargetHandle); } context.ExecuteCommandBuffer(cmd); cmd.Clear(); ConfigureCameraTarget(colorTargetHandle, depthTargetHandle); if (hasPostProcess) { colorGradingLutPass.ConfigureDescriptor(in postProcessingData, out var desc, out var filterMode); RenderingUtils.ReAllocateHandleIfNeeded(ref m_PostProcessPasses.m_ColorGradingLut, desc, filterMode, TextureWrapMode.Clamp, name: "_InternalGradingLut"); colorGradingLutPass.Setup(colorGradingLutHandle); EnqueuePass(colorGradingLutPass); } m_Render2DLightingPass.Setup(renderPassInputs.requiresDepthTexture || m_UseDepthStencilBuffer); // Disable obsolete warning for internal usage #pragma warning disable CS0618 m_Render2DLightingPass.ConfigureTarget(colorTargetHandle, depthTargetHandle); #pragma warning restore CS0618 EnqueuePass(m_Render2DLightingPass); bool shouldRenderUI = cameraData.rendersOverlayUI; bool outputToHDR = cameraData.isHDROutputActive; if (shouldRenderUI && outputToHDR) { m_DrawOffscreenUIPass.Setup(cameraData, k_DepthStencilFormat); EnqueuePass(m_DrawOffscreenUIPass); } // TODO: Investigate how to make FXAA work with HDR output. bool isFXAAEnabled = cameraData.antialiasing == AntialiasingMode.FastApproximateAntialiasing && !outputToHDR; // When using Upscale Render Texture on a Pixel Perfect Camera, we want all post-processing effects done with a low-res RT, // and only upscale the low-res RT to fullscreen when blitting it to camera target. Also, final post processing pass is not run in this case, // so FXAA is not supported (you don't want to apply FXAA when everything is intentionally pixelated). bool requireFinalPostProcessPass = lastCameraInStack && !ppcUpscaleRT && stackHasPostProcess && isFXAAEnabled; bool hasPassesAfterPostProcessing = activeRenderPassQueue.Find(x => x.renderPassEvent == RenderPassEvent.AfterRenderingPostProcessing) != null; bool needsColorEncoding = DebugHandler == null || !DebugHandler.HDRDebugViewIsActive(cameraData.resolveFinalTarget); if (hasPostProcess) { var desc = PostProcessPass.GetCompatibleDescriptor(cameraTargetDescriptor, cameraTargetDescriptor.width, cameraTargetDescriptor.height, cameraTargetDescriptor.graphicsFormat, DepthBits.None); RenderingUtils.ReAllocateHandleIfNeeded(ref m_PostProcessPasses.m_AfterPostProcessColor, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_AfterPostProcessTexture"); postProcessPass.Setup( cameraTargetDescriptor, colorTargetHandle, afterPostProcessColorHandle, depthTargetHandle, colorGradingLutHandle, requireFinalPostProcessPass, afterPostProcessColorHandle.nameID == k_CameraTarget.nameID && needsColorEncoding); EnqueuePass(postProcessPass); } RTHandle finalTargetHandle = colorTargetHandle; if (ppc != null && ppc.enabled && ppc.cropFrame != PixelPerfectCamera.CropFrame.None) { EnqueuePass(m_PixelPerfectBackgroundPass); // Queue PixelPerfect UpscalePass. Only used when using the Stretch Fill option if (ppc.requiresUpscalePass) { int upscaleWidth = ppc.refResolutionX * ppc.pixelRatio; int upscaleHeight = ppc.refResolutionY * ppc.pixelRatio; m_UpscalePass.Setup(colorTargetHandle, upscaleWidth, upscaleHeight, ppc.finalBlitFilterMode, cameraData.cameraTargetDescriptor, out finalTargetHandle); EnqueuePass(m_UpscalePass); } } if (requireFinalPostProcessPass) { finalPostProcessPass.SetupFinalPass(finalTargetHandle, hasPassesAfterPostProcessing, needsColorEncoding); EnqueuePass(finalPostProcessPass); } else if (lastCameraInStack && finalTargetHandle != k_CameraTarget) { m_FinalBlitPass.Setup(cameraTargetDescriptor, finalTargetHandle); EnqueuePass(m_FinalBlitPass); } // We can explicitely render the overlay UI from URP when HDR output is not enabled. // SupportedRenderingFeatures.active.rendersUIOverlay should also be set to true. if (shouldRenderUI && !outputToHDR) { EnqueuePass(m_DrawOverlayUIPass); } } public override void SetupCullingParameters(ref ScriptableCullingParameters cullingParameters, ref CameraData cameraData) { cullingParameters.cullingOptions = CullingOptions.None; cullingParameters.isOrthographic = cameraData.camera.orthographic; cullingParameters.shadowDistance = 0.0f; var cullResult = m_Renderer2DData.lightCullResult as Light2DCullResult; cullResult.SetupCulling(ref cullingParameters, cameraData.camera); } internal override void SwapColorBuffer(CommandBuffer cmd) { m_ColorBufferSystem.Swap(); // Disable obsolete warning for internal usage #pragma warning disable CS0618 //Check if we are using the depth that is attached to color buffer if (m_DepthTextureHandle.nameID != BuiltinRenderTextureType.CameraTarget) ConfigureCameraTarget(m_ColorBufferSystem.GetBackBuffer(cmd), m_DepthTextureHandle); else ConfigureCameraColorTarget(m_ColorBufferSystem.GetBackBuffer(cmd)); #pragma warning restore CS0618 m_ColorTextureHandle = m_ColorBufferSystem.GetBackBuffer(cmd); cmd.SetGlobalTexture("_CameraColorTexture", m_ColorTextureHandle.nameID); //Set _AfterPostProcessTexture, users might still rely on this although it is now always the cameratarget due to swapbuffer cmd.SetGlobalTexture("_AfterPostProcessTexture", m_ColorTextureHandle.nameID); } [Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)] internal override RTHandle GetCameraColorFrontBuffer(CommandBuffer cmd) { return m_ColorBufferSystem.GetFrontBuffer(cmd); } [Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)] internal override RTHandle GetCameraColorBackBuffer(CommandBuffer cmd) { return m_ColorBufferSystem.GetBackBuffer(cmd); } internal override void EnableSwapBufferMSAA(bool enable) { m_ColorBufferSystem.EnableMSAA(enable); } internal static bool IsGLDevice() { return SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore; } internal static bool supportsMRT { get => !IsGLDevice(); } internal override bool supportsNativeRenderPassRendergraphCompiler { get => !IsGLDevice(); // GLES and doesn't support MSAA resolve with the NRP API } } }