using System; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.Universal.Internal; namespace UnityEngine.Rendering.Universal { /// /// Draw screen space overlay UI into the given color and depth target /// internal class DrawScreenSpaceUIPass : ScriptableRenderPass { PassData m_PassData; RTHandle m_ColorTarget; RTHandle m_DepthTarget; // Whether to render on an offscreen render texture or on the current active render target bool m_RenderOffscreen; static readonly int s_CameraDepthTextureID = Shader.PropertyToID("_CameraDepthTexture"); static readonly int s_CameraOpaqueTextureID = Shader.PropertyToID("_CameraOpaqueTexture"); /// /// Creates a new DrawScreenSpaceUIPass instance. /// /// The RenderPassEvent to use. /// public DrawScreenSpaceUIPass(RenderPassEvent evt, bool renderOffscreen) { profilingSampler = ProfilingSampler.Get(URPProfileId.DrawScreenSpaceUI); renderPassEvent = evt; useNativeRenderPass = false; m_RenderOffscreen = renderOffscreen; m_PassData = new PassData(); } /// /// Get a descriptor for the required color texture for this pass. /// /// Camera target descriptor. /// Unscaled pixel width of the camera. /// Unscaled pixel height of the camera. /// public static void ConfigureColorDescriptor(ref RenderTextureDescriptor descriptor, int cameraWidth, int cameraHeight) { descriptor.graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB; descriptor.depthStencilFormat = GraphicsFormat.None; descriptor.width = cameraWidth; descriptor.height = cameraHeight; } /// /// Get a descriptor for the required depth texture for this pass. /// /// Camera target descriptor. /// Depth stencil format required. /// Unscaled pixel width of the camera. /// Unscaled pixel height of the camera. /// public static void ConfigureDepthDescriptor(ref RenderTextureDescriptor descriptor, GraphicsFormat depthStencilFormat, int cameraWidth, int cameraHeight) { descriptor.graphicsFormat = GraphicsFormat.None; descriptor.depthStencilFormat = depthStencilFormat; descriptor.width = cameraWidth; descriptor.height = cameraHeight; } private static void ExecutePass(RasterCommandBuffer commandBuffer, PassData passData, RendererList rendererList) { commandBuffer.DrawRendererList(rendererList); } // Specific to RG cases which have to go through Unsafe commands private static void ExecutePass(UnsafeCommandBuffer commandBuffer, UnsafePassData passData, RendererList rendererList) { commandBuffer.DrawRendererList(rendererList); } // Non-RenderGraph path public void Dispose() { m_ColorTarget?.Release(); m_DepthTarget?.Release(); } /// /// Configure the pass with the off-screen destination color texture and depth texture to execute the pass on. /// /// Camera rendering data containing all relevant render target information. /// Depth stencil format required for depth/stencil effects. public void Setup(UniversalCameraData cameraData, GraphicsFormat depthStencilFormat) { if (m_RenderOffscreen) { RenderTextureDescriptor colorDescriptor = cameraData.cameraTargetDescriptor; ConfigureColorDescriptor(ref colorDescriptor, cameraData.pixelWidth, cameraData.pixelHeight); RenderingUtils.ReAllocateHandleIfNeeded(ref m_ColorTarget, colorDescriptor, name: "_OverlayUITexture"); RenderTextureDescriptor depthDescriptor = cameraData.cameraTargetDescriptor; ConfigureDepthDescriptor(ref depthDescriptor, depthStencilFormat, cameraData.pixelWidth, cameraData.pixelHeight); RenderingUtils.ReAllocateHandleIfNeeded(ref m_DepthTarget, depthDescriptor, name: "_OverlayUITexture_Depth"); } } /// [Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)] public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { if(m_RenderOffscreen) { // Disable obsolete warning for internal usage #pragma warning disable CS0618 ConfigureTarget(m_ColorTarget, m_DepthTarget); ConfigureClear(ClearFlag.Color, Color.clear); #pragma warning restore CS0618 cmd?.SetGlobalTexture(ShaderPropertyId.overlayUITexture, m_ColorTarget); } else { UniversalCameraData cameraData = renderingData.frameData.Get(); DebugHandler debugHandler = GetActiveDebugHandler(cameraData); bool resolveToDebugScreen = debugHandler != null && debugHandler.WriteToDebugScreenTexture(cameraData.resolveFinalTarget); if (resolveToDebugScreen) { // Disable obsolete warning for internal usage #pragma warning disable CS0618 ConfigureTarget(debugHandler.DebugScreenColorHandle, debugHandler.DebugScreenDepthHandle); #pragma warning restore CS0618 } else { // Get RTHandle alias to use RTHandle apis var cameraTarget = RenderingUtils.GetCameraTargetIdentifier(ref renderingData); RTHandleStaticHelpers.SetRTHandleStaticWrapper(cameraTarget); var colorTargetHandle = RTHandleStaticHelpers.s_RTHandleWrapper; // Disable obsolete warning for internal usage #pragma warning disable CS0618 ConfigureTarget(colorTargetHandle); #pragma warning restore CS0618 } } } /// [Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)] public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { using (new ProfilingScope(renderingData.commandBuffer, profilingSampler)) { RendererList rendererList = context.CreateUIOverlayRendererList(renderingData.cameraData.camera); ExecutePass(CommandBufferHelpers.GetRasterCommandBuffer(renderingData.commandBuffer), m_PassData, rendererList); } } //RenderGraph path private class PassData { internal RendererListHandle rendererList; } // Specific to RG cases which have to go through Unsafe commands private class UnsafePassData { internal RendererListHandle rendererList; internal TextureHandle colorTarget; } internal void RenderOffscreen(RenderGraph renderGraph, ContextContainer frameData, GraphicsFormat depthStencilFormat, out TextureHandle output) { UniversalCameraData cameraData = frameData.Get(); RenderTextureDescriptor colorDescriptor = cameraData.cameraTargetDescriptor; ConfigureColorDescriptor(ref colorDescriptor, cameraData.pixelWidth, cameraData.pixelHeight); output = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorDescriptor, "_OverlayUITexture", true); RenderTextureDescriptor depthDescriptor = cameraData.cameraTargetDescriptor; ConfigureDepthDescriptor(ref depthDescriptor, depthStencilFormat, cameraData.pixelWidth, cameraData.pixelHeight); TextureHandle depthBuffer = UniversalRenderer.CreateRenderGraphTexture(renderGraph, depthDescriptor, "_OverlayUITexture_Depth", false); // Render uGUI and UIToolkit overlays using (var builder = renderGraph.AddRasterRenderPass("Draw Screen Space UIToolkit/uGUI - Offscreen", out var passData, profilingSampler)) { builder.SetRenderAttachment(output, 0); passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.UIToolkit_UGUI); builder.UseRendererList(passData.rendererList); builder.SetRenderAttachmentDepth(depthBuffer, AccessFlags.ReadWrite); if (output.IsValid()) builder.SetGlobalTextureAfterPass(output, ShaderPropertyId.overlayUITexture); builder.SetRenderFunc((PassData data, RasterGraphContext context) => { ExecutePass(context.cmd, data, data.rendererList); }); } // Render IMGUI overlay and software cursor in a UnsafePass // Doing so allow us to safely cover cases when graphics commands called through onGUI() in user scripts are not supported by RenderPass API // Besides, Vulkan backend doesn't support SetSRGWrite() in RenderPass API and we have some of them at IMGUI levels // Note, these specific UI calls doesn't need depth buffer unlike UIToolkit/uGUI using (var builder = renderGraph.AddUnsafePass("Draw Screen Space IMGUI/SoftwareCursor - Offscreen", out var passData, profilingSampler)) { passData.colorTarget = output; builder.UseTexture(output, AccessFlags.Write); passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.LowLevel); builder.UseRendererList(passData.rendererList); builder.SetRenderFunc((UnsafePassData data, UnsafeGraphContext context) => { context.cmd.SetRenderTarget(data.colorTarget); ExecutePass(context.cmd, data, data.rendererList); }); } } internal void RenderOverlay(RenderGraph renderGraph, ContextContainer frameData, in TextureHandle colorBuffer, in TextureHandle depthBuffer) { UniversalCameraData cameraData = frameData.Get(); UniversalResourceData resourceData = frameData.Get(); UniversalRenderer renderer = cameraData.renderer as UniversalRenderer; // Render uGUI and UIToolkit overlays using (var builder = renderGraph.AddRasterRenderPass("Draw UIToolkit/uGUI Overlay", out var passData, profilingSampler)) { if (cameraData.requiresOpaqueTexture && renderer != null) builder.UseGlobalTexture(s_CameraOpaqueTextureID); builder.SetRenderAttachment(colorBuffer, 0); builder.SetRenderAttachmentDepth(depthBuffer, AccessFlags.ReadWrite); passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.UIToolkit_UGUI); builder.UseRendererList(passData.rendererList); builder.SetRenderFunc((PassData data, RasterGraphContext context) => { ExecutePass(context.cmd, data, data.rendererList); }); } // Render IMGUI overlay and software cursor in a UnsafePass // Doing so allow us to safely cover cases when graphics commands called through onGUI() in user scripts are not supported by RenderPass API // Besides, Vulkan backend doesn't support SetSRGWrite() in RenderPass API and we have some of them at IMGUI levels // Note, these specific UI calls doesn't need depth buffer unlike UIToolkit/uGUI using (var builder = renderGraph.AddUnsafePass("Draw IMGUI/SoftwareCursor Overlay", out var passData, profilingSampler)) { passData.colorTarget = colorBuffer; builder.UseTexture(colorBuffer, AccessFlags.Write); passData.rendererList = renderGraph.CreateUIOverlayRendererList(cameraData.camera, UISubset.LowLevel); builder.UseRendererList(passData.rendererList); builder.SetRenderFunc((UnsafePassData data, UnsafeGraphContext context) => { context.cmd.SetRenderTarget(data.colorTarget); ExecutePass(context.cmd, data, data.rendererList); }); } } } }