[URP 15] Blitting a RenderTexture to screen -- probably obvious

TL;DR: How to blit from RenderTexture asset onto screen using RTHandle API?

I’m making a pixel art game.

  • The camera renders to a lo-res RenderTexture.

  • A custom render pass applies a post-processing material.

  • The same custom render pass blits the processed texture to the screen.

In older versions of URP, I wrote this:

// src is the lo-res RenderTexture.
// dest is the camera's color target.
Blit(src, dest, material);

I have tried converting this to the new APIs.

// In the feature file:
public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
{
    _retroDrawPass.ConfigureInput(ScriptableRenderPassInput.Color);
    _retroDrawPass.SetTarget(renderer.cameraColorTargetHandle, drawSettings.src);
}

// In the pass file:
public void SetTarget(RTHandle colorHandle, RenderTexture src)
{
    // These are both of type RTHandle.
    _colorTarget = colorHandle;
    _srcTarget = RTHandles.Alloc(src);
}

public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
    // After setup and profiling...

    Blitter.BlitCameraTexture(cmd, _srcTarget, _colorTarget, _passSettings.postMat, 0);
}

This results in three errors.

[Error] Assertion failed
// The assertion is this: Debug.Assert(handle.m_ExternalTexture != null || handle.rt != null);
// It fails when checking _srcTarget. However, logging _srcTarget.rt == null immediately before blitting prints "False."

[Error] ArgumentNullException: Value cannot be null.
// Once again, this is _srcTarget. See above.

[Warning] Render Pipeline error : the XR layout still contains active passes. Executing XRSystem.EndLayout() right now.
// This is not an XR game, and I am not using any XR features. I don't quite understand this one.

I am probably using RTHandles incorrectly. I have combed through documentation to no avail. Some more details:

  • I have tried calling ConfigureTarget for _colorTarget as well as for both _colorTarget and _srcTarget. No difference.
  • I get the same errors when writing Blitter.BlitCameraTexture(cmd, _colorTarget, _colorTarget, _passSettings.postMat, 0);. This suggests that it has nothing to do with _srcTarget.
  • These errors also appear when running Unity’s example pass.
  • This is the closest I’ve come to success:
Blitter.BlitCameraTexture(cmd, _srcTarget, _srcTarget, _passSettings.postMat, 0);
Blitter.BlitCameraTexture(cmd, _srcTarget, _colorTarget);

This results in the image being drawn to the screen. The material is not applied.

Please advise – I have probably made an obvious mistake.

A side note - if you are using a custom shader you will need to update it for use with the new Blit API, because the inputs for shader programs are different (it is not backwards compatible with old shaders). Check the core blit material shaders for an example of how to change vert/frag. This may be part of your woes.

(Apologies for not giving more detail, currently not at my pc)

After narrowing down what is a bug and what isn’t, I have partially solved the problem.

  • Unity’s own Full Screen Pass prints the same three errors. This is a known bug.

  • I circumvented this issue by configuring the Renderer asset. Set “Intermediate Texture” to Always. My custom pass (detailed above) works fine with this toggle enabled.

  • The shader provided here did not work for me. (ElliotB is correct, though. Unity’s example doesn’t work, for some reason.)

  • Yes, in my case I needed to blit a specific pre-existing render texture, whereas this shader accesses the camera’s opaque texture directly. It didn’t work even when I tried to just blit the opaque texture.

  • What did work: URP Sample Buffer node in Shader Graph. I recreated my shader in Shader Graph, and it works fine.

  • The way this interacts with Render Textures is strange. The render texture is drawn in the lower left corner independent of screen resolution. It does not scale or stretch. No manipulation of the Blit methods changes this. (I grabbed the method from the package cache and made an overload with scaling. No luck.)

  • I circumvented this in the shader by manipulating the UVs. I’m not satisfied with this solution.

My inexperience with graphics programming is likely showing. I will provide future updates as I discover more. Even still, I hope this helps future internet searchers.

6 Likes