using System; using System.Collections.Generic; using XCEngine; namespace XCEngine.Rendering { internal enum RenderGraphRasterPassExecutionKind { None = 0, ColorScaleFullscreen = 1, ShaderVectorFullscreen = 2, FinalColorFullscreen = 3 } public sealed class RenderGraphRasterPassBuilder { private struct TextureBindingRequest { public string shaderResourceName; public RenderGraphTextureHandle texture; public bool isDepth; } private readonly ScriptableRenderContext m_context; private readonly string m_passName; private readonly List m_readTextures = new List(); private readonly List m_readDepthTextures = new List(); private readonly List m_textureBindings = new List(); private readonly List m_colorAttachments = new List(); private RenderGraphTextureHandle m_sourceColorTexture; private RenderGraphTextureHandle m_depthAttachment; private Vector4 m_vectorPayload; private string m_shaderPath = string.Empty; private string m_shaderPassName = string.Empty; private FinalColorSettings m_finalColorSettings; private RenderGraphRasterPassExecutionKind m_executionKind; private bool m_finalized; internal RenderGraphRasterPassBuilder( ScriptableRenderContext context, string passName) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (string.IsNullOrEmpty(passName)) { throw new ArgumentException( "Render graph raster pass name cannot be null or empty.", nameof(passName)); } m_context = context; m_passName = passName; } public RenderGraphRasterPassBuilder UseColorSource( RenderGraphTextureHandle texture) { m_sourceColorTexture = texture; return this; } public RenderGraphRasterPassBuilder SetFinalColorFullscreenExecution( FinalColorSettings settings) { m_executionKind = RenderGraphRasterPassExecutionKind .FinalColorFullscreen; m_finalColorSettings = settings; m_shaderPath = string.Empty; m_shaderPassName = string.Empty; return this; } public RenderGraphRasterPassBuilder UseTexture( RenderGraphTextureHandle texture) { AddReadTexture( texture, isDepth: false); return this; } public RenderGraphRasterPassBuilder UseDepthTexture( RenderGraphTextureHandle texture) { AddReadTexture( texture, isDepth: true); return this; } public RenderGraphRasterPassBuilder BindTexture( string shaderResourceName, RenderGraphTextureHandle texture) { AddTextureBinding( shaderResourceName, texture, isDepth: false); return this; } public RenderGraphRasterPassBuilder BindDepthTexture( string shaderResourceName, RenderGraphTextureHandle texture) { AddTextureBinding( shaderResourceName, texture, isDepth: true); return this; } public RenderGraphRasterPassBuilder SetColorAttachment( RenderGraphTextureHandle texture, int index = 0) { if (index < 0) { throw new ArgumentOutOfRangeException( nameof(index), "Render graph color attachment index cannot be negative."); } while (m_colorAttachments.Count <= index) { m_colorAttachments.Add(default); } m_colorAttachments[index] = texture; return this; } public RenderGraphRasterPassBuilder SetDepthAttachment( RenderGraphTextureHandle texture) { m_depthAttachment = texture; return this; } public RenderGraphRasterPassBuilder SetColorScaleFullscreenExecution( Vector4 colorScale) { m_executionKind = RenderGraphRasterPassExecutionKind .ColorScaleFullscreen; m_vectorPayload = colorScale; m_shaderPath = string.Empty; m_shaderPassName = string.Empty; return this; } public RenderGraphRasterPassBuilder SetShaderVectorFullscreenExecution( string shaderPath, Vector4 vectorPayload, string shaderPassName = null) { if (string.IsNullOrEmpty(shaderPath)) { throw new ArgumentException( "Fullscreen shader path cannot be null or empty.", nameof(shaderPath)); } m_executionKind = RenderGraphRasterPassExecutionKind .ShaderVectorFullscreen; m_vectorPayload = vectorPayload; m_shaderPath = shaderPath; m_shaderPassName = shaderPassName ?? string.Empty; return this; } public bool Commit() { if (m_finalized || !HasExecutionConfigured() || !HasAnyColorAttachment()) { return false; } m_finalized = true; ulong nativePassHandle = InternalCalls .Rendering_ScriptableRenderContext_BeginRasterPass( m_context.nativeHandle, m_passName); if (nativePassHandle == 0ul) { return false; } if (m_sourceColorTexture.isValid && !InternalCalls .Rendering_ScriptableRenderContext_SetRasterPassSourceColorTexture( m_context.nativeHandle, nativePassHandle, m_sourceColorTexture.nativeIndex)) { return false; } for (int i = 0; i < m_readTextures.Count; ++i) { RenderGraphTextureHandle readTexture = m_readTextures[i]; if (!readTexture.isValid) { continue; } if (!InternalCalls .Rendering_ScriptableRenderContext_AddRasterPassReadTexture( m_context.nativeHandle, nativePassHandle, readTexture.nativeIndex)) { return false; } } for (int i = 0; i < m_readDepthTextures.Count; ++i) { RenderGraphTextureHandle readDepthTexture = m_readDepthTextures[i]; if (!readDepthTexture.isValid) { continue; } if (!InternalCalls .Rendering_ScriptableRenderContext_AddRasterPassReadDepthTexture( m_context.nativeHandle, nativePassHandle, readDepthTexture.nativeIndex)) { return false; } } for (int i = 0; i < m_textureBindings.Count; ++i) { TextureBindingRequest binding = m_textureBindings[i]; if (!binding.texture.isValid || string.IsNullOrEmpty( binding.shaderResourceName)) { continue; } if (!InternalCalls .Rendering_ScriptableRenderContext_AddRasterPassTextureBinding( m_context.nativeHandle, nativePassHandle, binding.shaderResourceName, binding.texture.nativeIndex, binding.isDepth)) { return false; } } for (int i = 0; i < m_colorAttachments.Count; ++i) { RenderGraphTextureHandle colorAttachment = m_colorAttachments[i]; if (!colorAttachment.isValid) { continue; } if (!InternalCalls .Rendering_ScriptableRenderContext_SetRasterPassColorAttachment( m_context.nativeHandle, nativePassHandle, i, colorAttachment.nativeIndex)) { return false; } } if (m_depthAttachment.isValid && !InternalCalls .Rendering_ScriptableRenderContext_SetRasterPassDepthAttachment( m_context.nativeHandle, nativePassHandle, m_depthAttachment.nativeIndex)) { return false; } bool configuredExecution; switch (m_executionKind) { case RenderGraphRasterPassExecutionKind .ColorScaleFullscreen: configuredExecution = InternalCalls .Rendering_ScriptableRenderContext_SetRasterPassColorScaleFullscreenExecution( m_context.nativeHandle, nativePassHandle, ref m_vectorPayload); break; case RenderGraphRasterPassExecutionKind .ShaderVectorFullscreen: configuredExecution = InternalCalls .Rendering_ScriptableRenderContext_SetRasterPassShaderVectorFullscreenExecution( m_context.nativeHandle, nativePassHandle, m_shaderPath, m_shaderPassName, ref m_vectorPayload); break; case RenderGraphRasterPassExecutionKind .FinalColorFullscreen: configuredExecution = InternalCalls .Rendering_ScriptableRenderContext_SetRasterPassFinalColorFullscreenExecution( m_context.nativeHandle, nativePassHandle, ref m_finalColorSettings); break; default: configuredExecution = false; break; } return configuredExecution && InternalCalls .Rendering_ScriptableRenderContext_CommitRasterPass( m_context.nativeHandle, nativePassHandle); } private bool HasExecutionConfigured() { return m_executionKind != RenderGraphRasterPassExecutionKind.None; } private void AddReadTexture( RenderGraphTextureHandle texture, bool isDepth) { if (!texture.isValid) { return; } List readList = isDepth ? m_readDepthTextures : m_readTextures; for (int i = 0; i < readList.Count; ++i) { if (readList[i].nativeIndex == texture.nativeIndex) { return; } } readList.Add(texture); } private void AddTextureBinding( string shaderResourceName, RenderGraphTextureHandle texture, bool isDepth) { if (string.IsNullOrEmpty(shaderResourceName)) { throw new ArgumentException( "Shader resource name cannot be null or empty.", nameof(shaderResourceName)); } if (!texture.isValid) { return; } AddReadTexture( texture, isDepth); for (int i = 0; i < m_textureBindings.Count; ++i) { TextureBindingRequest existingBinding = m_textureBindings[i]; if (!string.Equals( existingBinding.shaderResourceName, shaderResourceName, StringComparison.Ordinal)) { continue; } existingBinding.texture = texture; existingBinding.isDepth = isDepth; m_textureBindings[i] = existingBinding; return; } m_textureBindings.Add( new TextureBindingRequest { shaderResourceName = shaderResourceName, texture = texture, isDepth = isDepth }); } private bool HasAnyColorAttachment() { for (int i = 0; i < m_colorAttachments.Count; ++i) { if (m_colorAttachments[i].isValid) { return true; } } return false; } } }