using System; using UnityEngine.Rendering.RenderGraphModule; using System.Runtime.CompilerServices; // AggressiveInlining namespace UnityEngine.Rendering.Universal { internal sealed class SmaaPostProcessPass : PostProcessPass { public const string k_TargetName = "_SMAATarget"; Material m_Material; bool m_IsValid; // Static resource textures for SMAA technique Texture2D m_AreaTexture; Texture2D m_SearchTexture; Experimental.Rendering.GraphicsFormat m_SMAAEdgeFormat; AntialiasingQuality m_AntiAliasingQuality; public SmaaPostProcessPass(Shader shader, Texture2D smaaAreaTexture, Texture2D smaaSearchTexture) { this.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing - 1; this.profilingSampler = null; m_Material = PostProcessUtils.LoadShader(shader, passName); m_IsValid = m_Material != null; m_AreaTexture = smaaAreaTexture; m_SearchTexture = smaaSearchTexture; // SMAA // Only two components are needed for edge render texture, but on some vendors four components may be faster. if (SystemInfo.IsFormatSupported(Experimental.Rendering.GraphicsFormat.R8G8_UNorm, Experimental.Rendering.GraphicsFormatUsage.Render) && SystemInfo.graphicsDeviceVendor.ToLowerInvariant().Contains("arm")) m_SMAAEdgeFormat = Experimental.Rendering.GraphicsFormat.R8G8_UNorm; else m_SMAAEdgeFormat = Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm; } public override void Dispose() { CoreUtils.Destroy(m_Material); m_IsValid = false; } public void Setup(AntialiasingQuality antialiasingQuality) { m_AntiAliasingQuality = antialiasingQuality; } private class SMAASetupPassData { internal Vector4 metrics; internal Texture2D areaTexture; internal Texture2D searchTexture; internal float stencilRef; internal float stencilMask; internal AntialiasingQuality antialiasingQuality; internal Material material; internal TextureHandle sourceTexture; } private class SMAAPassData { internal TextureHandle sourceTexture; internal TextureHandle blendTexture; internal Material material; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { if (!m_IsValid) return; UniversalResourceData resourceData = frameData.Get(); var sourceTexture = resourceData.cameraColor; var destDesc = renderGraph.GetTextureDesc(sourceTexture); var destinationTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, destDesc, k_TargetName, true, FilterMode.Bilinear); destDesc.clearColor = Color.black; destDesc.clearColor.a = 0.0f; var edgeTextureDesc = destDesc; edgeTextureDesc.format = m_SMAAEdgeFormat; var edgeTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, edgeTextureDesc, "_EdgeStencilTexture", true, FilterMode.Bilinear); var edgeTextureStencilDesc = destDesc; edgeTextureStencilDesc.format = Experimental.Rendering.GraphicsFormatUtility.GetDepthStencilFormat(24); var edgeTextureStencil = PostProcessUtils.CreateCompatibleTexture(renderGraph, edgeTextureStencilDesc, "_EdgeTexture", true, FilterMode.Bilinear); var blendTextureDesc = destDesc; blendTextureDesc.format = Experimental.Rendering.GraphicsFormat.R8G8B8A8_UNorm; var blendTexture = PostProcessUtils.CreateCompatibleTexture(renderGraph, blendTextureDesc, "_BlendTexture", true, FilterMode.Point); using (var builder = renderGraph.AddRasterRenderPass("SMAA Edge Detection", out var passData, ProfilingSampler.Get(URPProfileId.RG_SMAAEdgeDetection))) { // Material setup const int kStencilBit = 64; // TODO RENDERGRAPH: handle dynamic scaling passData.metrics = new Vector4(1f / destDesc.width, 1f / destDesc.height, destDesc.width, destDesc.height); // TODO: These are constant for every frame passData.areaTexture = m_AreaTexture; passData.searchTexture = m_SearchTexture; passData.stencilRef = (float)kStencilBit; passData.stencilMask = (float)kStencilBit; passData.antialiasingQuality = m_AntiAliasingQuality; passData.material = m_Material; builder.SetRenderAttachment(edgeTexture, 0, AccessFlags.Write); builder.SetRenderAttachmentDepth(edgeTextureStencil, AccessFlags.Write); passData.sourceTexture = sourceTexture; builder.UseTexture(sourceTexture, AccessFlags.Read); builder.UseTexture(resourceData.cameraDepth ,AccessFlags.Read); builder.SetRenderFunc(static (SMAASetupPassData data, RasterGraphContext context) => { var SMAAMaterial = data.material; var cmd = context.cmd; RTHandle sourceTextureHdl = data.sourceTexture; // Pass 0: Material Setup SetupMaterial(data); // Pass 1: Edge detection Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one; Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, SMAAMaterial, ShaderPass.k_EdgeDetection); }); } using (var builder = renderGraph.AddRasterRenderPass("SMAA Blend weights", out var passData, ProfilingSampler.Get(URPProfileId.RG_SMAABlendWeight))) { builder.SetRenderAttachment(blendTexture, 0, AccessFlags.Write); builder.SetRenderAttachmentDepth(edgeTextureStencil, AccessFlags.Read); passData.sourceTexture = edgeTexture; builder.UseTexture(edgeTexture, AccessFlags.Read); passData.material = m_Material; builder.SetRenderFunc(static (SMAAPassData data, RasterGraphContext context) => { var SMAAMaterial = data.material; var cmd = context.cmd; RTHandle sourceTextureHdl = data.sourceTexture; // Pass 2: Blend weights Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one; Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, SMAAMaterial, ShaderPass.k_BlendWeightsCalculation); }); } using (var builder = renderGraph.AddRasterRenderPass("SMAA Neighborhood blending", out var passData, ProfilingSampler.Get(URPProfileId.RG_SMAANeighborhoodBlend))) { builder.SetRenderAttachment(destinationTexture, 0, AccessFlags.Write); passData.sourceTexture = sourceTexture; builder.UseTexture(sourceTexture, AccessFlags.Read); passData.blendTexture = blendTexture; builder.UseTexture(blendTexture, AccessFlags.Read); passData.material = m_Material; builder.SetRenderFunc(static (SMAAPassData data, RasterGraphContext context) => { var SMAAMaterial = data.material; var cmd = context.cmd; RTHandle sourceTextureHdl = data.sourceTexture; // Pass 3: Neighborhood blending SMAAMaterial.SetTexture(ShaderConstants._BlendTexture, data.blendTexture); Vector2 viewportScale = sourceTextureHdl.useScaling ? new Vector2(sourceTextureHdl.rtHandleProperties.rtHandleScale.x, sourceTextureHdl.rtHandleProperties.rtHandleScale.y) : Vector2.one; Blitter.BlitTexture(cmd, sourceTextureHdl, viewportScale, SMAAMaterial, ShaderPass.k_NeighborhoodBlending); }); } resourceData.cameraColor = destinationTexture; } static void SetupMaterial(SMAASetupPassData data) { using (new ProfilingScope(ProfilingSampler.Get(URPProfileId.RG_SMAAMaterialSetup))) { // Common for all passes data.material.SetVector(ShaderConstants._Metrics, data.metrics); data.material.SetTexture(ShaderConstants._AreaTexture, data.areaTexture); data.material.SetTexture(ShaderConstants._SearchTexture, data.searchTexture); data.material.SetFloat(ShaderConstants._StencilRef, data.stencilRef); data.material.SetFloat(ShaderConstants._StencilMask, data.stencilMask); // Quality presets data.material.shaderKeywords = null; switch (data.antialiasingQuality) { case AntialiasingQuality.Low: data.material.EnableKeyword(ShaderKeywordStrings.SmaaLow); break; case AntialiasingQuality.Medium: data.material.EnableKeyword(ShaderKeywordStrings.SmaaMedium); break; case AntialiasingQuality.High: data.material.EnableKeyword(ShaderKeywordStrings.SmaaHigh); break; } } } // Precomputed shader ids to same some CPU cycles (mostly affects mobile) public static class ShaderConstants { public static readonly int _Metrics = Shader.PropertyToID("_Metrics"); public static readonly int _AreaTexture = Shader.PropertyToID("_AreaTexture"); public static readonly int _SearchTexture = Shader.PropertyToID("_SearchTexture"); public static readonly int _BlendTexture = Shader.PropertyToID("_BlendTexture"); //public static readonly int _EdgeTexture = Shader.PropertyToID("_EdgeTexture"); public static readonly int _StencilRef = Shader.PropertyToID("_StencilRef"); public static readonly int _StencilMask = Shader.PropertyToID("_StencilMask"); } public static class ShaderPass { public const int k_EdgeDetection = 0; public const int k_BlendWeightsCalculation = 1; public const int k_NeighborhoodBlending = 2; } } }