using UnityEngine.Experimental.GlobalIllumination; using UnityEngine.Experimental.Rendering; using UnityEngine.Profiling; using Unity.Collections; using UnityEngine.Experimental.Rendering.RenderGraphModule; namespace UnityEngine.Rendering.Universal.Internal { // Render all tiled-based deferred lights. internal class GBufferPass : ScriptableRenderPass { static readonly int s_CameraNormalsTextureID = Shader.PropertyToID("_CameraNormalsTexture"); static ShaderTagId s_ShaderTagLit = new ShaderTagId("Lit"); static ShaderTagId s_ShaderTagSimpleLit = new ShaderTagId("SimpleLit"); static ShaderTagId s_ShaderTagUnlit = new ShaderTagId("Unlit"); static ShaderTagId s_ShaderTagUniversalGBuffer = new ShaderTagId("UniversalGBuffer"); static ShaderTagId s_ShaderTagUniversalMaterialType = new ShaderTagId("UniversalMaterialType"); ProfilingSampler m_ProfilingSampler = new ProfilingSampler("Render GBuffer"); DeferredLights m_DeferredLights; static ShaderTagId[] s_ShaderTagValues; static RenderStateBlock[] s_RenderStateBlocks; FilteringSettings m_FilteringSettings; RenderStateBlock m_RenderStateBlock; private PassData m_PassData; public GBufferPass(RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference, DeferredLights deferredLights) { base.profilingSampler = new ProfilingSampler(nameof(GBufferPass)); base.renderPassEvent = evt; m_PassData = new PassData(); m_DeferredLights = deferredLights; m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask); m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing); m_RenderStateBlock.stencilState = stencilState; m_RenderStateBlock.stencilReference = stencilReference; m_RenderStateBlock.mask = RenderStateMask.Stencil; if (s_ShaderTagValues == null) { s_ShaderTagValues = new ShaderTagId[4]; s_ShaderTagValues[0] = s_ShaderTagLit; s_ShaderTagValues[1] = s_ShaderTagSimpleLit; s_ShaderTagValues[2] = s_ShaderTagUnlit; s_ShaderTagValues[3] = new ShaderTagId(); // Special catch all case for materials where UniversalMaterialType is not defined or the tag value doesn't match anything we know. } if (s_RenderStateBlocks == null) { s_RenderStateBlocks = new RenderStateBlock[4]; s_RenderStateBlocks[0] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialLit); s_RenderStateBlocks[1] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialSimpleLit); s_RenderStateBlocks[2] = DeferredLights.OverwriteStencil(m_RenderStateBlock, (int)StencilUsage.MaterialMask, (int)StencilUsage.MaterialUnlit); s_RenderStateBlocks[3] = s_RenderStateBlocks[0]; } } public void Dispose() { m_DeferredLights.ReleaseGbufferResources(); } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { RTHandle[] gbufferAttachments = m_DeferredLights.GbufferAttachments; if (cmd != null) { var allocateGbufferDepth = true; if (m_DeferredLights.UseRenderPass && (m_DeferredLights.DepthCopyTexture != null && m_DeferredLights.DepthCopyTexture.rt != null)) { m_DeferredLights.GbufferAttachments[m_DeferredLights.GbufferDepthIndex] = m_DeferredLights.DepthCopyTexture; allocateGbufferDepth = false; } // Create and declare the render targets used in the pass for (int i = 0; i < gbufferAttachments.Length; ++i) { // Lighting buffer has already been declared with line ConfigureCameraTarget(m_ActiveCameraColorAttachment.Identifier(), ...) in DeferredRenderer.Setup if (i == m_DeferredLights.GBufferLightingIndex) continue; // Normal buffer may have already been created if there was a depthNormal prepass before. // DepthNormal prepass is needed for forward-only materials when SSAO is generated between gbuffer and deferred lighting pass. if (i == m_DeferredLights.GBufferNormalSmoothnessIndex && m_DeferredLights.HasNormalPrepass) continue; if (i == m_DeferredLights.GbufferDepthIndex && !allocateGbufferDepth) continue; // No need to setup temporaryRTs if we are using input attachments as they will be Memoryless if (m_DeferredLights.UseRenderPass && i != m_DeferredLights.GBufferRenderingLayers && (i != m_DeferredLights.GbufferDepthIndex && !m_DeferredLights.HasDepthPrepass)) continue; m_DeferredLights.ReAllocateGBufferIfNeeded(cameraTextureDescriptor, i); cmd.SetGlobalTexture(m_DeferredLights.GbufferAttachments[i].name, m_DeferredLights.GbufferAttachments[i].nameID); } } if (m_DeferredLights.UseRenderPass) m_DeferredLights.UpdateDeferredInputAttachments(); ConfigureTarget(m_DeferredLights.GbufferAttachments, m_DeferredLights.DepthAttachment, m_DeferredLights.GbufferFormats); // We must explicitly specify we don't want any clear to avoid unwanted side-effects. // ScriptableRenderer will implicitly force a clear the first time the camera color/depth targets are bound. ConfigureClear(ClearFlag.None, Color.black); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { var cmd = renderingData.commandBuffer; m_PassData.filteringSettings = m_FilteringSettings; using (new ProfilingScope(cmd, m_ProfilingSampler)) { m_PassData.deferredLights = m_DeferredLights; // User can stack several scriptable renderers during rendering but deferred renderer should only lit pixels added by this gbuffer pass. // If we detect we are in such case (camera is in overlay mode), we clear the highest bits of stencil we have control of and use them to // mark what pixel to shade during deferred pass. Gbuffer will always mark pixels using their material types. ref CameraData cameraData = ref renderingData.cameraData; ShaderTagId lightModeTag = s_ShaderTagUniversalGBuffer; m_PassData.drawingSettings = CreateDrawingSettings(lightModeTag, ref renderingData, renderingData.cameraData.defaultOpaqueSortFlags); ExecutePass(context, m_PassData, ref renderingData); // If any sub-system needs camera normal texture, make it available. // Input attachments will only be used when this is not needed so safe to skip in that case if (!m_DeferredLights.UseRenderPass) renderingData.commandBuffer.SetGlobalTexture(s_CameraNormalsTextureID, m_DeferredLights.GbufferAttachments[m_DeferredLights.GBufferNormalSmoothnessIndex]); } } static void ExecutePass(ScriptableRenderContext context, PassData data, ref RenderingData renderingData, bool useRenderGraph = false) { bool usesRenderingLayers = data.deferredLights.UseRenderingLayers && !data.deferredLights.HasNormalPrepass; if (usesRenderingLayers) CoreUtils.SetKeyword(renderingData.commandBuffer, ShaderKeywordStrings.WriteRenderingLayers, true); context.ExecuteCommandBuffer(renderingData.commandBuffer); renderingData.commandBuffer.Clear(); if (data.deferredLights.IsOverlay) { data.deferredLights.ClearStencilPartial(renderingData.commandBuffer); context.ExecuteCommandBuffer(renderingData.commandBuffer); renderingData.commandBuffer.Clear(); } NativeArray tagValues = new NativeArray(s_ShaderTagValues, Allocator.Temp); NativeArray stateBlocks = new NativeArray(s_RenderStateBlocks, Allocator.Temp); context.DrawRenderers(renderingData.cullResults, ref data.drawingSettings, ref data.filteringSettings, s_ShaderTagUniversalMaterialType, false, tagValues, stateBlocks); tagValues.Dispose(); stateBlocks.Dispose(); // Render objects that did not match any shader pass with error shader RenderingUtils.RenderObjectsWithError(context, ref renderingData.cullResults, renderingData.cameraData.camera, data.filteringSettings, SortingCriteria.None); // If any sub-system needs camera normal texture, make it available. // Input attachments will only be used when this is not needed so safe to skip in that case if (!data.deferredLights.UseRenderPass) renderingData.commandBuffer.SetGlobalTexture(s_CameraNormalsTextureID, data.deferredLights.GbufferAttachments[data.deferredLights.GBufferNormalSmoothnessIndex]); // Clean up if (usesRenderingLayers) { CoreUtils.SetKeyword(renderingData.commandBuffer, ShaderKeywordStrings.WriteRenderingLayers, false); context.ExecuteCommandBuffer(renderingData.commandBuffer); renderingData.commandBuffer.Clear(); } } private class PassData { internal TextureHandle[] gbuffer; internal TextureHandle depth; internal RenderingData renderingData; internal DeferredLights deferredLights; internal FilteringSettings filteringSettings; internal DrawingSettings drawingSettings; } internal void Render(RenderGraph renderGraph, TextureHandle cameraColor, TextureHandle cameraDepth, ref RenderingData renderingData, ref UniversalRenderer.RenderGraphFrameResources frameResources) { using (var builder = renderGraph.AddRenderPass("GBuffer Pass", out var passData, m_ProfilingSampler)) { passData.gbuffer = frameResources.gbuffer = m_DeferredLights.GbufferTextureHandles; for (int i = 0; i < m_DeferredLights.GBufferSliceCount; i++) { var gbufferSlice = renderingData.cameraData.cameraTargetDescriptor; gbufferSlice.depthBufferBits = 0; // make sure no depth surface is actually created gbufferSlice.stencilFormat = GraphicsFormat.None; if (i != m_DeferredLights.GBufferLightingIndex) { gbufferSlice.graphicsFormat = m_DeferredLights.GetGBufferFormat(i); frameResources.gbuffer[i] = UniversalRenderer.CreateRenderGraphTexture(renderGraph, gbufferSlice, DeferredLights.k_GBufferNames[i], true); } else { frameResources.gbuffer[i] = cameraColor; } passData.gbuffer[i] = builder.UseColorBuffer(frameResources.gbuffer[i], i); } passData.deferredLights = m_DeferredLights; passData.depth = builder.UseDepthBuffer(cameraDepth, DepthAccess.Write); passData.renderingData = renderingData; builder.AllowPassCulling(false); passData.filteringSettings = m_FilteringSettings; ShaderTagId lightModeTag = s_ShaderTagUniversalGBuffer; passData.drawingSettings = CreateDrawingSettings(lightModeTag, ref passData.renderingData, renderingData.cameraData.defaultOpaqueSortFlags); builder.SetRenderFunc((PassData data, RenderGraphContext context) => { ExecutePass(context.renderContext, data, ref data.renderingData, true); }); } using (var builder = renderGraph.AddRenderPass("Set GBuffer Globals", out var passData, m_ProfilingSampler)) { passData.gbuffer = frameResources.gbuffer = m_DeferredLights.GbufferTextureHandles; for (int i = 0; i < m_DeferredLights.GBufferSliceCount; i++) { passData.gbuffer[i] = builder.UseColorBuffer(frameResources.gbuffer[i], i); } passData.depth = builder.UseDepthBuffer(cameraDepth, DepthAccess.Read); passData.renderingData = renderingData; passData.deferredLights = m_DeferredLights; builder.AllowPassCulling(false); builder.SetRenderFunc((PassData data, RenderGraphContext context) => { for (int i = 0; i < data.gbuffer.Length; i++) { if (i != data.deferredLights.GBufferLightingIndex) data.renderingData.commandBuffer.SetGlobalTexture(DeferredLights.k_GBufferNames[i], data.gbuffer[i]); } }); } } } }