Using multiple input textures with RenderGraph.AddBlitPass

Hi. I’m trying to create a shader that needs to sample from more textures than the default _BlitTexture.
I want to achieve something like this:

RenderGraphUtils.BlitMaterialParameters pass1 = new( sourceTexture, someTexture, material, 0 );
renderGraph.AddBlitPass( pass1, "Custom pass 1" );

// Use the result of a previous pass as an additional input to the next one
material.SetTexture(someTextureId, someTexture);

RenderGraphUtils.BlitMaterialParameters pass2 = new( sourceTexture, anotherTexture, material, 1 );
// In this pass I want to sample from both sourceTexture and someTexture
renderGraph.AddBlitPass( pass, "Custom pass 2" );

but when I try to bind the texture using material.SetTexture(someTextureId, someTexture) I’m getting this error Current Render Graph Resource Registry is not set. You are probably trying to cast a Render Graph handle to a resource outside of a Render Graph Pass., a lot of repeated errors about Trying to use an invalid resource (pass DrawOpaqueObjects). and the whole screen is black.

Is it possible to do this while using AddBlitPass?

3 Likes

Hi. Did you ever find a solution to this? I am having the same issue. Specifically I am trying to use a texture from a previous render pass as a source texture to a Blit pass (ie., AddBlitPass). I’ve tried following this guide for passing textures between passes, I’ve tried the custom frame data approach as well as the global texture approach and neither of them work for me. I’m also not sure if it matters yet but I am trying to pass my texture into a shader written with ShaderGraph.

Lastly the other error I am getting a bunch of (Unity 6 - 6000.0.27f1) is:

InvalidOperationException: The previously scheduled job ZBinningJob writes to the Unity.Collections.NativeArray1[System.UInt32] ZBinningJob.bins. You must call JobHandle.Complete() on the job ZBinningJob, before you can write to the Unity.Collections.NativeArray1[System.UInt32] safely.

Follow Up: I think what is going on is that AddBlitPass is adding another pass (like its name says) so you can not use the builder to mark textures as being used for that pass. One possible work around is to mimic what AddBlitPass is doing to get more control over the pass specifics. It does seem however that the global texture approach should work though :confused:.
I’ll keep this thread updated on my outcome.

Hi. I didn’t find a solution to this so I put this part of the project on hold for now. Passing the output textures from a previous pass to next worked for me like that:

RenderGraphUtils.BlitMaterialParameters pass1 = new( sourceTexture, destinationTexture, material, 0 );
renderGraph.AddBlitPass( pass1, "Custom pass 1" );

RenderGraphUtils.BlitMaterialParameters pass2 = new( destinationTexture, anotherDestinationTexture, material, 1 );
// In this pass I want to sample from both sourceTexture and someTexture
renderGraph.AddBlitPass( pass2, "Custom pass 2" );

I also was getting the ZBinningJob errors but just assumed they will disappear when fixing the other errors.

Regarding mimicing AddBlitPass I tried but couldn’t get it to work. I remeber reading something about the global textures so I might try that when I come back to this if there’s no other solution.

Good news (at least for me), I was able to fix it by creating my own version of AddBlitPass and passing textures with ContextContainer.Create<> / ContextContainer.Get<>.

A few things I had to make sure I was doing:

  • Set the material texture properties in the render function set with builder.SetRenderFunc. If I tried doing this in the builder block I would get the zbinning errors.
  • Mark the textures your using with builder.UseTexture or else you’ll get resource invalid errors.

Here was my final render function that mimic’d a simple version of AddBlitPass:

static void ExecutePass(PassData data, UnsafeGraphContext context)
{
    CommandBuffer unsafeCmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);

    // materialProperties holds static Shader.PropertyToID fields
    data.material.SetTexture(materialProperties.blitTexture, data.source);
    data.material.SetTexture(materialProperties.someTexture, data.someTexture);

    context.cmd.SetRenderTarget(data.destination, 0, CubemapFace.Unknown, 0);
    // data.mesh is a fullscreen quad that I created, its vertices go from -1,1 to 1,1 (NDC)
    context.cmd.DrawMesh(data.mesh, Matrix4x4.identity, data.material, 0, 0, null);
}

I hope this helps!

Edit: I’m not 100% sure if I need to be using the unsafe pass here (same as AddBlitPass). A quick change threw errors so I’m leaving it for now but its something to look into.

Thanks for pointing me in the right direction. All I had to do was to set the texture in the ExecutePass function instead of AddBlitPass.

Here’s my version of AddBlitPass without the unsafe pass and without the fullscreen quad:

void ExecutePass( PassData data, RasterGraphContext context )
{
    foreach ( TextureBindInfo info in data.additionalTextures )
    {
        material.SetTexture( info.slot, info.texture );
    }

    Blitter.BlitTexture( context.cmd, data.source, new Vector4( 1, 1, 0, 0 ), material, data.passIndex );
}

void AddBlitPass( RenderGraph renderGraph, TextureHandle source, TextureHandle destination, string passName, int passIndex, params TextureBindInfo[] additionalTextures )
{
    using var builder = renderGraph.AddRasterRenderPass( passName, out PassData passData );

    builder.UseTexture( source );
    passData.source = source;
    passData.passIndex = passIndex;

    foreach ( TextureBindInfo info in additionalTextures )
    {
        Assert.IsTrue( info.texture.IsValid() );
        builder.UseTexture( info.texture );
    }

    passData.additionalTextures = additionalTextures;

    builder.SetRenderAttachment( destination, 0 );
    builder.SetRenderFunc< PassData >( ExecutePass );
}

public class TextureBindInfo
{
    public int slot;
    public TextureHandle texture;
}

class PassData
{
    public TextureHandle source;
    public TextureBindInfo[] additionalTextures;
    public int passIndex;
}
5 Likes

Nice, glad you got it working!

Hello,

I’m begining with RenderGraph and try to achieve something similar but I don’t understand what should be in your RecordRenderGraph. Is it only AddBlitPass ? And how do you manage to set your additionalTextures ?
I’m only getting an error :
“Trying to use an invalid resource (pass DrawOpaqueObjects)”

Thanks for your help

Hi. Here’s the general idea how to use this. The RecordRenderGraph should look something like this:

public override void RecordRenderGraph( RenderGraph renderGraph, ContextContainer frameData )
{
    UniversalResourceData resourceData = frameData.Get< UniversalResourceData >();
    UniversalCameraData cameraData = frameData.Get< UniversalCameraData >();

    if ( resourceData.isActiveTargetBackBuffer )
    {
        return;
    }

    RenderTextureDescriptor renderTextureDesc = cameraData.cameraTargetDescriptor;
    renderTextureDesc.depthBufferBits = 0;

    TextureHandle sceneColor = resourceData.activeColorTexture;
    TextureHandle texture1 = UniversalRenderer.CreateRenderGraphTexture( renderGraph, renderTextureDesc, "Texture 1", false );
    TextureHandle texture2 = UniversalRenderer.CreateRenderGraphTexture( renderGraph, renderTextureDesc, "Texture 2", false );

    AddBlitPass( renderGraph, sceneColor, texture1, "Render only red channel to texture 1", 0 );
    int additionalTextureId = Shader.PropertyToID( "_AdditionalTexture" );
    TextureBindInfo info = new TextureBindInfo { slot = additionalTextureId, texture = sceneColor };
    // This pass will have texture1 bound to _BlitTexture and sceneColor bound to the _AdditionalTexture in your shader
    // _BlitTexture is the default name for the source texture and _AdditionalTexture is a parameter you define so it can have whatever name
    AddBlitPass( renderGraph, texture1, texture2, "Render red and green channel to texture 2", 1, info ); 

    // sceneColor texture is the texture that is finally displayed on the screen so now we just render back to sceneColor to display our results
    renderGraph.AddBlitPass( texture2, sceneColor, Vector2.one, Vector2.zero );
}

And here is an example shader:

Shader "CustomEffects/ExampleShader"
{
    SubShader
    {
        Blend One Zero
        Tags
        {
            "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"
        }
        LOD 100
        ZWrite Off Cull Off

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"

        TEXTURE2D( _AdditionalTexture );

        #define SAMPLE_BLIT(uv) SAMPLE_TEXTURE2D( _BlitTexture, sampler_LinearClamp, uv )
        ENDHLSL

        Pass
        {
            Name "Pass 0"

            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Pass0

            float4 Pass0( Varyings input ) : SV_Target
            {
                float2 uv = input.texcoord;
                float red = SAMPLE_BLIT( uv ).r;
                return float4( red, 0, 0, 1 );
            }
            ENDHLSL
        }

        Pass
        {
            Name "Pass 1"

            HLSLPROGRAM
            #pragma vertex Vert
            #pragma fragment Pass1

            float4 Pass1( Varyings input ) : SV_Target
            {
                float2 uv = input.texcoord;
                float red = SAMPLE_BLIT( uv ).r; // This samples from the result of the previous pass
                float green = SAMPLE_TEXTURE2D( _AdditionalTexture, sampler_LinearClamp, uv ).g
                return float4( red, green, 0, 1 );
            }
            ENDHLSL
        }
    }
}

Of course you can do much more in the RecordRenderGraph function like dynamically changing shader parameters (by calling material.SetFloat etc) or dynamically adding or removing entire passes.
(Small disclaimer: I just wrote this code in a text editor without testing it in engine because I haven’t used unity for a few months and don’t have it installed right now but I think it should work fine)

1 Like