// SPDX-License-Identifier: MIT #if GS_ENABLE_URP using UnityEngine; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; #if UNITY_6000_0_OR_NEWER using UnityEngine.Rendering.RenderGraphModule; #endif namespace GaussianSplatting.Runtime { // Note: I have no idea what is the purpose of ScriptableRendererFeature vs ScriptableRenderPass, which one of those // is supposed to do resource management vs logic, etc. etc. Code below "seems to work" but I'm just fumbling along, // without understanding any of it. // // ReSharper disable once InconsistentNaming class GaussianSplatURPFeature : ScriptableRendererFeature { class GSRenderPass : ScriptableRenderPass { const string GaussianSplatRTName = "_GaussianSplatRT"; #if !UNITY_6000_0_OR_NEWER RTHandle m_RenderTarget; internal ScriptableRenderer m_Renderer; internal CommandBuffer m_Cmb; #endif public void Dispose() { #if !UNITY_6000_0_OR_NEWER m_RenderTarget?.Release(); #endif } #if UNITY_6000_0_OR_NEWER const string ProfilerTag = "GaussianSplatRenderGraph"; static readonly ProfilingSampler s_profilingSampler = new(ProfilerTag); static readonly int s_gaussianSplatRT = Shader.PropertyToID(GaussianSplatRTName); class PassData { internal UniversalCameraData CameraData; internal TextureHandle SourceTexture; internal TextureHandle SourceDepth; internal TextureHandle GaussianSplatRT; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { using var builder = renderGraph.AddUnsafePass(ProfilerTag, out PassData passData); var cameraData = frameData.Get(); var resourceData = frameData.Get(); RenderTextureDescriptor rtDesc = cameraData.cameraTargetDescriptor; rtDesc.depthBufferBits = 0; rtDesc.msaaSamples = 1; rtDesc.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat; var textureHandle = UniversalRenderer.CreateRenderGraphTexture(renderGraph, rtDesc, GaussianSplatRTName, true); passData.CameraData = cameraData; passData.SourceTexture = resourceData.activeColorTexture; passData.SourceDepth = resourceData.activeDepthTexture; passData.GaussianSplatRT = textureHandle; builder.UseTexture(resourceData.activeColorTexture, AccessFlags.ReadWrite); builder.UseTexture(resourceData.activeDepthTexture); builder.UseTexture(textureHandle, AccessFlags.Write); builder.AllowPassCulling(false); builder.SetRenderFunc(static (PassData data, UnsafeGraphContext context) => { var commandBuffer = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd); using var _ = new ProfilingScope(commandBuffer, s_profilingSampler); commandBuffer.SetGlobalTexture(s_gaussianSplatRT, data.GaussianSplatRT); CoreUtils.SetRenderTarget(commandBuffer, data.GaussianSplatRT, data.SourceDepth, ClearFlag.Color, Color.clear); Material matComposite = GaussianSplatRenderSystem.instance.SortAndRenderSplats(data.CameraData.camera, commandBuffer); commandBuffer.BeginSample(GaussianSplatRenderSystem.s_ProfCompose); Blitter.BlitCameraTexture(commandBuffer, data.GaussianSplatRT, data.SourceTexture, matComposite, 0); commandBuffer.EndSample(GaussianSplatRenderSystem.s_ProfCompose); }); } #else public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { RenderTextureDescriptor rtDesc = renderingData.cameraData.cameraTargetDescriptor; rtDesc.depthBufferBits = 0; rtDesc.msaaSamples = 1; rtDesc.graphicsFormat = GraphicsFormat.R16G16B16A16_SFloat; RenderingUtils.ReAllocateIfNeeded(ref m_RenderTarget, rtDesc, FilterMode.Point, TextureWrapMode.Clamp, name: GaussianSplatRTName); cmd.SetGlobalTexture(m_RenderTarget.name, m_RenderTarget.nameID); ConfigureTarget(m_RenderTarget, m_Renderer.cameraDepthTargetHandle); ConfigureClear(ClearFlag.Color, new Color(0,0,0,0)); } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if (m_Cmb == null) return; // add sorting, view calc and drawing commands for each splat object Material matComposite = GaussianSplatRenderSystem.instance.SortAndRenderSplats(renderingData.cameraData.camera, m_Cmb); // compose m_Cmb.BeginSample(GaussianSplatRenderSystem.s_ProfCompose); Blitter.BlitCameraTexture(m_Cmb, m_RenderTarget, m_Renderer.cameraColorTargetHandle, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store, matComposite, 0); m_Cmb.EndSample(GaussianSplatRenderSystem.s_ProfCompose); context.ExecuteCommandBuffer(m_Cmb); } #endif } GSRenderPass m_Pass; bool m_HasCamera; public override void Create() { m_Pass = new GSRenderPass { renderPassEvent = RenderPassEvent.BeforeRenderingTransparents }; } public override void OnCameraPreCull(ScriptableRenderer renderer, in CameraData cameraData) { m_HasCamera = false; var system = GaussianSplatRenderSystem.instance; if (!system.GatherSplatsForCamera(cameraData.camera)) return; #if !UNITY_6000_0_OR_NEWER CommandBuffer cmb = system.InitialClearCmdBuffer(cameraData.camera); m_Pass.m_Cmb = cmb; #endif m_HasCamera = true; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (!m_HasCamera) return; #if !UNITY_6000_0_OR_NEWER m_Pass.m_Renderer = renderer; #endif renderer.EnqueuePass(m_Pass); } protected override void Dispose(bool disposing) { m_Pass?.Dispose(); m_Pass = null; } } } #endif // #if GS_ENABLE_URP