Passing Textures Between Passes

I’m trying to write a base class for creating any number of render passes, where each feeds their final output texture to the next pass. I have the first pass working, it just blits a texture with a material. what I’m trying to figure out is how in subsequent passes I can read the previous pass texture. I know I can use ContextContainer.Get<T> . more info in docs here but that depends on a type and doesn’t support duplicate instances (which I need to support). I could generate a bunch of global textures with unique names, but I’d like to avoid that for performance reasons. I’ve tried to use frame data, but none of it seems to contain data about the previous pass, I suppose because it’s been cleared? Maybe I could disable clearing? Or maybe I can store the pass textures in a separate data structure that’s accessible without using ContextContainer?

This example might be what you need Graphics/Packages/com.unity.render-pipelines.universal/Samples~/URPRenderGraphSamples/BlitWithMaterial/BlitAndSwapColorRendererFeature.cs at master · Unity-Technologies/Graphics · GitHub

Thanks for your response. In ScriptableRenderPass.requiresIntermediateTexture I noticed this warning:

“Using this option might have a significant performance impact on untethered VR platforms.”

I’m targeting visionOS. With that in mind, would you advise a different approach?

For untethered VR, ideally you draw directly to the backbuffer. URP can do this if you don’t use an intermediate texture (and no post processing, no HDR, etc).

Does your first pass need to read from cameraColor? Then you need an intermediate texture, no way around that. You can’t read directly from the backbuffer. And that intermediate texture has a performance cost.

I don’t need to read cameraColor, I’m just blitting the same texture with different materials in several passes in similar ways, but in arbitrary runtime order. So I don’t want to have to copy/paste a lot of the same code between passes. I think I have something working with a CustomData class that exists on its own outside of a ScriptableRenderPass that I use to transfer the blitted texture between passes.

using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;

public class CustomData : ContextItem
{
    //Texture we can fetch in a later render pass.
    public TextureHandle transferredTexture;

    //Reset the texture when the frame resets.
    public override void Reset()
    {
        transferredTexture = TextureHandle.nullHandle;
    }
}

Then I pass an instance of CustomData to my base ScriptableRenderPass class I use for all my passes. If it’s not the last pass, I transfer the texture to the CustomData instance. If it’s not the first pass, I read the transferred texture.

        public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
        {
            using (var builder = renderGraph.AddRasterRenderPass<PassData>(_passName, out var passData))
            {
                //Source Texture
                if (firstPass)
                {
                    // Create a render texture from the source texture
                    _textureToImportRTHandle = RTHandles.Alloc(_textureToImport);
                    // Importing a texture to use as a source
                    TextureHandle importedTexture = renderGraph.ImportTexture(_textureToImportRTHandle);
                    builder.UseTexture(importedTexture, AccessFlags.Read);
                    // Set source texture for ExecutePass to use
                    passData.sourceTexture = importedTexture;
                }
                else() //Not the first pass, so read the transferred texture 
                {
                    var previousPassTexture = _customData.transferredTexture;
                    builder.UseTexture(previousPassTexture, AccessFlags.Read);
                    //Set source texture for ExecutePass to use
                    passData.sourceTexture = previousPassTexture;
                }

                //Target Texture
                TextureHandle targetTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, _sourceTextureDesc, _targetTextureName, false);

                if (!lastPass) //not the last pass, so transfer the texture
                {
                    //Store target texture for frame data to be used in other render passes
                    _customData.transferredTexture = targetTexture;
                }

                //Set the target texture as the render target
                builder.SetRenderAttachment(targetTexture, 0, AccessFlags.ReadWrite);
                ...
}

Do you think this is a decent approach? Thanks for the help.

hmm. wondering if maybe the FramebufferFetch example is a better approach to this?