When can I release my compute buffer in my scriptable render feature?

public class SplashFeature : ScriptableRendererFeature {
class SplashPass : ScriptableRenderPass {
private string profilerTag = “SplashFeature”;
internal ComputeBuffer splashBuffer = new ComputeBuffer(1, 28);

		// This method is called before executing the render pass.
		// It can be used to configure render targets and their clear state. Also to create temporary render target textures.
		// When empty this render pass will render to the active camera render target.
		// You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
		// The render pipeline will ensure target setup and clearing happens in an performance manner.
		public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { }

		// Here you can implement the rendering logic.
		// Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
		// https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
		// You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
		public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) {
			CommandBuffer cmd = CommandBufferPool.Get(profilerTag);
			using (new ProfilingScope(cmd, new ProfilingSampler(profilerTag))) {
				context.ExecuteCommandBuffer(cmd);
				cmd.Clear();

				int count = Splashes.splashes.Count;

				splashBuffer = new ComputeBuffer(count != 0 ? count : 1, 28);

				ProlateSpheroid[] data = {default};

				if (count != 0) {
					int i = 0;
					data = new ProlateSpheroid[count];
					foreach (Splash splash in Splashes.splashes) {
						data *= splash.Shape;*
  •  				i++;*
    
  •  			}*
    
  •  		}*
    
  •  		splashBuffer.SetData(data);*
    
  •  		cmd.SetGlobalBuffer("_SplashBuffer", splashBuffer);*
    
  •  		cmd.SetGlobalInt("_SplashCount", Splashes.splashes.Count);*
    
  •  	}*
    
  •  	context.ExecuteCommandBuffer(cmd);*
    
  •  	CommandBufferPool.Release(cmd);*
    
  •  }*
    
  •  /// Cleanup any allocated resources that were created during the execution of this render pass.*
    
  •  public override void FrameCleanup(CommandBuffer cmd) {*
    
  •  	//splashBuffer.Release();*
    
  •  }*
    
  • }*

  • private SplashPass pass;*

  • public override void Create() {*

  •  pass = new SplashPass {renderPassEvent = RenderPassEvent.BeforeRendering};*
    
  • }*

  • public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) {*

  •  renderer.EnqueuePass(pass);*
    
  • }*

  • public void OnDisable() {*

  •  //pass.splashBuffer.Release();*
    
  • }*
    }
    I’ve got this scriptable render feature to set a global compute buffer that one of my shaders needs. It works, but it produces an endless stream of “Garbage Collector disposing of Compute Buffer” warnings.
    I know that I’m supposed to use either Release() or Dispose() to get rid of the buffers after I’m done with them, but I’m not sure when that is. I’ve tried ScriptableRenderPass.FrameCleanup(), it breaks my shader and there’s still a couple of the warnings at the beggining. I’ve tried ScriptableRenderFeature.OnDisable(), and that doesn’t seem to do anything. I also need to declare a new ComputeBuffer every frame, unless there’s some other way to change its size. So I can’t just initialize it once and use the same one always. (The data I’m trying to store in the buffer is of a variable amount).

I had similar issue with ‘local’ compute buffer with a renderer feature.
here is how I solved my problem.

Two things you have to check

1. instantiated your compute buffer once and re-use it.
I instantiated my compute buffer on OnCameraSetup and I found that this function can be called multiple times. So, If you don’t check whether the compute buffer is null or not, compute buffer will be instantiated every time and the previous one get to GC which makes GC Warning.

2. overrides ‘protected Dispose()’ of ScriptableRendererFeature and dispose all compute buffers of ScriptableRendererPass.


Here is simple sample code. I hope this can help you.

using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.UI;

public class DebugRendererFeature : ScriptableRendererFeature
{
    class CustomRenderPass : ScriptableRenderPass
    {
        private ComputeBuffer debugBuffer;
        private RenderTexture targetRT;
        private ComputeShader Cs;
        private RenderTargetIdentifier RtHandle;

        public CustomRenderPass(RenderTexture rt, ComputeShader cs)
        {
            targetRT = rt;
            Cs = cs;

            RtHandle = new RenderTargetIdentifier(targetRT);
        }
        
        public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
        {
            if (debugBuffer == null)
            {
                debugBuffer = new ComputeBuffer(1, sizeof(float));
            }
            debugBuffer.SetData(new float[]{1.0f});
        }
        
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get();

            int Kernel = Cs.FindKernel("CSMain");
            cmd.SetComputeBufferParam(Cs, Kernel, "DebugBuffer", debugBuffer);
            cmd.SetComputeTextureParam(Cs, Kernel, "Result", RtHandle);
            
            cmd.DispatchCompute(Cs, Kernel, targetRT.width / 8, targetRT.height / 8, 1);
            
            context.ExecuteCommandBuffer(cmd);
            
            cmd.Clear();
        }

        public override void OnCameraCleanup(CommandBuffer cmd)
        {
        }
        
        public void Dispose()
        {
            debugBuffer?.Dispose();
        }
    }

    CustomRenderPass m_ScriptablePass;
    
    [SerializeField]
    private ComputeShader Cs;

    public override void Create()
    {
        RenderTexture rt = new RenderTexture(256, 256, 0, GraphicsFormat.R16G16B16A16_SFloat);
        rt.enableRandomWrite = true;
        rt.Create();
        
        m_ScriptablePass = new CustomRenderPass(rt, Cs);
        
        if (Resources.FindObjectsOfTypeAll<RawImage>()[0] != null)
        {
            Resources.FindObjectsOfTypeAll<RawImage>()[0].texture = rt;
        }

        m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
    }
    
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        renderer.EnqueuePass(m_ScriptablePass);
    }

    protected override void Dispose(bool disposing)
    {
        m_ScriptablePass.Dispose();
        base.Dispose(disposing);
    }
}