using System;
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Experimental.Rendering.RenderGraphModule;
using UnityEngine.Profiling;
namespace UnityEngine.Rendering.Universal.Internal
{
///
/// Extension of DrawObjectPass that also output Rendering Layers Texture as second render target.
///
internal class DrawObjectsWithRenderingLayersPass : DrawObjectsPass
{
RTHandle[] m_ColorTargetIndentifiers;
RTHandle m_DepthTargetIndentifiers;
public DrawObjectsWithRenderingLayersPass(URPProfileId profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference) :
base(profilerTag, opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference)
{
m_ColorTargetIndentifiers = new RTHandle[2];
}
public void Setup(RTHandle colorAttachment, RTHandle renderingLayersTexture, RTHandle depthAttachment)
{
if (colorAttachment == null)
throw new ArgumentException("Color attachment can not be null", "colorAttachment");
if (renderingLayersTexture == null)
throw new ArgumentException("Rendering layers attachment can not be null", "renderingLayersTexture");
if (depthAttachment == null)
throw new ArgumentException("Depth attachment can not be null", "depthAttachment");
m_ColorTargetIndentifiers[0] = colorAttachment;
m_ColorTargetIndentifiers[1] = renderingLayersTexture;
m_DepthTargetIndentifiers = depthAttachment;
}
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
ConfigureTarget(m_ColorTargetIndentifiers, m_DepthTargetIndentifiers);
}
protected override void OnExecute(CommandBuffer cmd)
{
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.WriteRenderingLayers, true);
}
}
///
/// Draw objects into the given color and depth target
///
/// You can use this pass to render objects that have a material and/or shader
/// with the pass names UniversalForward or SRPDefaultUnlit.
///
public class DrawObjectsPass : ScriptableRenderPass
{
FilteringSettings m_FilteringSettings;
RenderStateBlock m_RenderStateBlock;
List m_ShaderTagIdList = new List();
string m_ProfilerTag;
ProfilingSampler m_ProfilingSampler;
bool m_IsOpaque;
///
/// Used to indicate whether transparent objects should receive shadows or not.
///
public bool m_ShouldTransparentsReceiveShadows;
PassData m_PassData;
bool m_UseDepthPriming;
static readonly int s_DrawObjectPassDataPropID = Shader.PropertyToID("_DrawObjectPassData");
///
/// Creates a new DrawObjectsPass instance.
///
///
///
///
/// The RenderPassEvent to use.
///
///
///
///
///
///
///
///
///
public DrawObjectsPass(string profilerTag, ShaderTagId[] shaderTagIds, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
{
base.profilingSampler = new ProfilingSampler(nameof(DrawObjectsPass));
m_PassData = new PassData();
m_ProfilerTag = profilerTag;
m_ProfilingSampler = new ProfilingSampler(profilerTag);
foreach (ShaderTagId sid in shaderTagIds)
m_ShaderTagIdList.Add(sid);
renderPassEvent = evt;
m_FilteringSettings = new FilteringSettings(renderQueueRange, layerMask);
m_RenderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
m_IsOpaque = opaque;
m_ShouldTransparentsReceiveShadows = false;
if (stencilState.enabled)
{
m_RenderStateBlock.stencilReference = stencilReference;
m_RenderStateBlock.mask = RenderStateMask.Stencil;
m_RenderStateBlock.stencilState = stencilState;
}
}
///
/// Creates a new DrawObjectsPass instance.
///
///
///
///
///
///
///
///
///
///
///
///
public DrawObjectsPass(string profilerTag, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
: this(profilerTag,
new ShaderTagId[] { new ShaderTagId("SRPDefaultUnlit"), new ShaderTagId("UniversalForward"), new ShaderTagId("UniversalForwardOnly") },
opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference)
{ }
internal DrawObjectsPass(URPProfileId profileId, bool opaque, RenderPassEvent evt, RenderQueueRange renderQueueRange, LayerMask layerMask, StencilState stencilState, int stencilReference)
: this(profileId.GetType().Name, opaque, evt, renderQueueRange, layerMask, stencilState, stencilReference)
{
m_ProfilingSampler = ProfilingSampler.Get(profileId);
}
///
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
m_PassData.m_IsOpaque = m_IsOpaque;
m_PassData.m_RenderStateBlock = m_RenderStateBlock;
m_PassData.m_FilteringSettings = m_FilteringSettings;
m_PassData.m_ShaderTagIdList = m_ShaderTagIdList;
m_PassData.m_ProfilingSampler = m_ProfilingSampler;
m_PassData.pass = this;
CameraSetup(renderingData.commandBuffer, m_PassData, ref renderingData);
ExecutePass(context, m_PassData, ref renderingData, renderingData.cameraData.IsCameraProjectionMatrixFlipped());
}
private static void CameraSetup(CommandBuffer cmd, PassData data, ref RenderingData renderingData)
{
if (renderingData.cameraData.renderer.useDepthPriming && data.m_IsOpaque && (renderingData.cameraData.renderType == CameraRenderType.Base || renderingData.cameraData.clearDepth))
{
data.m_RenderStateBlock.depthState = new DepthState(false, CompareFunction.Equal);
data.m_RenderStateBlock.mask |= RenderStateMask.Depth;
}
else if (data.m_RenderStateBlock.depthState.compareFunction == CompareFunction.Equal)
{
data.m_RenderStateBlock.depthState = new DepthState(true, CompareFunction.LessEqual);
data.m_RenderStateBlock.mask |= RenderStateMask.Depth;
}
}
private static void ExecutePass(ScriptableRenderContext context, PassData data, ref RenderingData renderingData, bool yFlip)
{
var cmd = renderingData.commandBuffer;
using (new ProfilingScope(cmd, data.m_ProfilingSampler))
{
// Global render pass data containing various settings.
// x,y,z are currently unused
// w is used for knowing whether the object is opaque(1) or alpha blended(0)
Vector4 drawObjectPassData = new Vector4(0.0f, 0.0f, 0.0f, (data.m_IsOpaque) ? 1.0f : 0.0f);
cmd.SetGlobalVector(s_DrawObjectPassDataPropID, drawObjectPassData);
// scaleBias.x = flipSign
// scaleBias.y = scale
// scaleBias.z = bias
// scaleBias.w = unused
float flipSign = yFlip ? -1.0f : 1.0f;
Vector4 scaleBias = (flipSign < 0.0f)
? new Vector4(flipSign, 1.0f, -1.0f, 1.0f)
: new Vector4(flipSign, 0.0f, 1.0f, 1.0f);
cmd.SetGlobalVector(ShaderPropertyId.scaleBiasRt, scaleBias);
// Set a value that can be used by shaders to identify when AlphaToMask functionality may be active
// The material shader alpha clipping logic requires this value in order to function correctly in all cases.
float alphaToMaskAvailable = ((renderingData.cameraData.cameraTargetDescriptor.msaaSamples > 1) && data.m_IsOpaque) ? 1.0f : 0.0f;
cmd.SetGlobalFloat(ShaderPropertyId.alphaToMaskAvailable, alphaToMaskAvailable);
// TODO RENDERGRAPH: do this as a separate pass, so no need of calling OnExecute here...
data.pass.OnExecute(cmd);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
Camera camera = renderingData.cameraData.camera;
var sortFlags = (data.m_IsOpaque) ? renderingData.cameraData.defaultOpaqueSortFlags : SortingCriteria.CommonTransparent;
if (renderingData.cameraData.renderer.useDepthPriming && data.m_IsOpaque && (renderingData.cameraData.renderType == CameraRenderType.Base || renderingData.cameraData.clearDepth))
sortFlags = SortingCriteria.SortingLayer | SortingCriteria.RenderQueue | SortingCriteria.OptimizeStateChanges | SortingCriteria.CanvasOrder;
var filterSettings = data.m_FilteringSettings;
#if UNITY_EDITOR
// When rendering the preview camera, we want the layer mask to be forced to Everything
if (renderingData.cameraData.isPreviewCamera)
{
filterSettings.layerMask = -1;
}
#endif
DrawingSettings drawSettings = RenderingUtils.CreateDrawingSettings(data.m_ShaderTagIdList, ref renderingData, sortFlags);
var activeDebugHandler = GetActiveDebugHandler(ref renderingData);
if (activeDebugHandler != null)
{
activeDebugHandler.DrawWithDebugRenderState(context, cmd, ref renderingData, ref drawSettings, ref filterSettings, ref data.m_RenderStateBlock,
(ScriptableRenderContext ctx, ref RenderingData data, ref DrawingSettings ds, ref FilteringSettings fs, ref RenderStateBlock rsb) =>
{
ctx.DrawRenderers(data.cullResults, ref ds, ref fs, ref rsb);
});
}
else
{
context.DrawRenderers(renderingData.cullResults, ref drawSettings, ref filterSettings, ref data.m_RenderStateBlock);
// Render objects that did not match any shader pass with error shader
RenderingUtils.RenderObjectsWithError(context, ref renderingData.cullResults, camera, filterSettings, SortingCriteria.None);
}
// Clean up
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.WriteRenderingLayers, false);
context.ExecuteCommandBuffer(cmd);
cmd.Clear();
}
}
private class PassData
{
internal TextureHandle m_Albedo;
internal TextureHandle m_Depth;
internal RenderingData m_RenderingData;
internal bool m_IsOpaque;
internal RenderStateBlock m_RenderStateBlock;
internal FilteringSettings m_FilteringSettings;
internal List m_ShaderTagIdList;
internal ProfilingSampler m_ProfilingSampler;
internal bool m_ShouldTransparentsReceiveShadows;
internal DrawObjectsPass pass;
}
internal void Render(RenderGraph renderGraph, TextureHandle colorTarget, TextureHandle depthTarget, TextureHandle mainShadowsTexture, TextureHandle additionalShadowsTexture, ref RenderingData renderingData)
{
Camera camera = renderingData.cameraData.camera;
using (var builder = renderGraph.AddRenderPass("Draw Objects Pass", out var passData,
m_ProfilingSampler))
{
passData.m_Albedo = builder.UseColorBuffer(colorTarget, 0);
passData.m_Depth = builder.UseDepthBuffer(depthTarget, DepthAccess.Write);
if (mainShadowsTexture.IsValid())
builder.ReadTexture(mainShadowsTexture);
if (additionalShadowsTexture.IsValid())
builder.ReadTexture(additionalShadowsTexture);
passData.m_RenderingData = renderingData;
builder.AllowPassCulling(false);
passData.m_IsOpaque = m_IsOpaque;
passData.m_RenderStateBlock = m_RenderStateBlock;
passData.m_FilteringSettings = m_FilteringSettings;
passData.m_ShaderTagIdList = m_ShaderTagIdList;
passData.m_ProfilingSampler = m_ProfilingSampler;
passData.m_ShouldTransparentsReceiveShadows = m_ShouldTransparentsReceiveShadows;
passData.pass = this;
builder.SetRenderFunc((PassData data, RenderGraphContext context) =>
{
ref var renderingData = ref data.m_RenderingData;
// TODO RENDERGRAPH figure out where to put XR proj flip logic so that it can be auto handled in render graph
#if ENABLE_VR && ENABLE_XR_MODULE
if (renderingData.cameraData.xr.enabled)
{
// SetRenderTarget might alter the internal device state(winding order).
// Non-stereo buffer is already updated internally when switching render target. We update stereo buffers here to keep the consistency.
bool renderIntoTexture = data.m_Albedo != renderingData.cameraData.xr.renderTarget;
renderingData.cameraData.PushBuiltinShaderConstantsXR(renderingData.commandBuffer, renderIntoTexture);
XRSystemUniversal.MarkShaderProperties(renderingData.commandBuffer, renderingData.cameraData.xrUniversal, renderIntoTexture);
}
#endif
// Currently we only need to call this additional pass when the user
// doesn't want transparent objects to receive shadows
if (!data.m_IsOpaque && !data.m_ShouldTransparentsReceiveShadows)
TransparentSettingsPass.ExecutePass(context.cmd, data.m_ShouldTransparentsReceiveShadows);
bool yFlip = renderingData.cameraData.IsRenderTargetProjectionMatrixFlipped(data.m_Albedo, data.m_Depth);
CameraSetup(context.cmd, data, ref renderingData);
ExecutePass(context.renderContext, data, ref renderingData, yFlip);
});
}
}
///
/// Called before ExecutePass draws the objects.
///
/// The command buffer to use.
protected virtual void OnExecute(CommandBuffer cmd) { }
}
}