CommandBuffer.Blit in VR/XR

I would like to call out this tutorial which explains how to blit in URP with VR in single pass instanced mode:

https://docs.unity.cn/Packages/com.unity.render-pipelines.universal@12.1/manual/renderer-features/how-to-fullscreen-blit-in-xr-spi.html

The biggest takeaway is that this has become way too complicated. In standard pipeline VR with single pass instanced, all of this is un-necessary and one can simply do CommandBuffer.Blit, and everything just works.

What changed to require this amouont of complication for URP and VR? If this amount of complication is indeed necessary, surely Unity could detect URP is enabled and do the right thing in CommandBuffer.Blit…

I’d love a technical explanation from anyone here at Unity about this…

3 Likes

This is just one more instance of the constant lack of clarity about Blit in URP.

This blit example uses CommandBuffer.SetRenderTarget(); despite code comments and the official documentation *stating you should not use CommandBuffer.SetRenderTarget();

Unity has way too many Blit functions with very little guidance or examples of how to use them other than internal SRP code.

Graphics.Blit(); - Built-In RenderPipeline

CommandBuffer.Blit( RenderTargetIdentifier source, RenderTargetIdentifier dest, Material mat);
Summary: Add a “blit into a render texture” command.

ScriptableRenderPass.Blit( CommandBuffer cmd, RenderTargetIdentifier source, RenderTargetIdentifier destination, Material material = null, int passIndex = 0);
Summary: Add a blit command to the context for execution. This changes the active render target in the ScriptableRenderer to destination.

ScriptableRenderPass.Blit( CommandBuffer cmd, ref RenderingData data, Material material, int passIndex = 0);
Summary: Add a blit command to the context for execution. This applies the material to the color target.

RenderingUtils.Blit(); - See: Unity.Graphics Github
Blitter class - 17 different Blit functions using Texture, RenderTexture, RTHandle and so on

When it comes to how all of this relates to TextureArrays and XR, things get even more confusing. Especially since it seems you are forced into using texture arrays if you want to blit any temporary RT or global shader property to the screen or if you try to use compute shaders in your command buffer.

If the API needs to treat Tex2D as single-slice Tex2DArrays then do so behind the scenes rather than requiring those who aren’t using XR to dig through SRP code just to blit a triangle to the screen.

I digress, other forum posts across SRP highlight the issues better than I can here. It should not be this hard to BLIT.

This example is in HDRP but I’ve run into the same situation in URP in recent weeks.
HDRP Custom Render Pass SetGlobalTexture requires RenderTextureArray

*If it is supposed to mean you should never use CommandBuffer.SetRenderTarget within the Configure(); function, then that should be clarified.

3 Likes

Small update, somehow this whole time I never noticed that the texture macro being used has its definition changed in /ShaderLibrary/Core.hlsl.

#if defined(UNITY_STEREO_INSTANCING_ENABLED) || defined(UNITY_STEREO_MULTIVIEW_ENABLED)

#define SLICE_ARRAY_INDEX unity_StereoEyeIndex

#define TEXTURE2D_X(textureName) TEXTURE2D_ARRAY(textureName)
...
...
#else
#define SLICE_ARRAY_INDEX 0

#define TEXTURE2D_X(textureName) TEXTURE2D(textureName)
...
...
#endif

You aren’t forced to use arrays, but you do (seemingly) need to use the TEXTURE2D_X macro.

1 Like

How I’ve understood this, the complicated workaround became “current way to do it” because the SPI keywords used on cmd.Blit broke after moving to new XR framework on Unity 2020+. I think they’ve restored some of them on either 2021.2 or 2022.1 but cmd.Blit still remains broken with SPI (but I think on recent versions it at least doesn’t output that grey and black eye views anymore).

In ideal case, Unity could fix the cmd.Blit for good but what we have now is the workaround (which Unity itself has to use as well for URP’s own SSAO).

1 Like

Posting this here to properly link the two forum posts.

For more information and insight on blitting in SRP see this post .

It seems that the programmers of unity never communicated. How it is possible to have 100500 “Blit” methods with different behaviour?

2 Likes

Not to mention, I just followed this tutorial to the letter, and it has not worked…
I checked the Frame debugger, it seems to work but then FinalBlit pass seems to undo it. Any ideas?

URP: 12.1.2
OpenXR: 1.3.1
Unity 2021.2.5f1

1 Like

Chiming in, since this about the only thread on the subject!

I’ve been struggling with the same issues the past few days. When using Single Pass Instanced rendering I’d simply get a Gray/Black image on the left & right eye, respectively. That is when using ScriptableRenderPass.Blit to do anything. This otherwise worked fine in 2019.4.

Unity’s “ColorCopy” pass calls RenderingUtils.Blit, which uses cmd.DrawProcedural if XR rendering is in use, and cmd.Blit otherwise. Harsh irony is that this function is inaccessible because it’s internal. But adopting the same functionality does make a difference:

Execute function

if (renderingData.cameraData.xrRendering)
{
    cmd.SetRenderTarget(new RenderTargetIdentifier(cameraColorTarget, 0, CubemapFace.Unknown, -1), RenderBufferLoadAction.Load, RenderBufferStoreAction.Store);
    cmd.DrawProcedural(Matrix4x4.identity, Material, 0, MeshTopology.Quads, 4, 1, null);
}
else
{
    cmd.SetRenderTarget(cameraColorTarget);
    Blit(cmd, mainTexHandle.id, cameraColorTarget, Material, 0);
}

Off this bat this didn’t appear to work. The pass was blitting into the render target, but nothing appeared to write into it. Apparently because the quad’s vertices weren’t being properly transformed into clip space.

Shader
To make this work, certain functions have to be used in the vertex shader used by the blitting material:

struct FullScreenAttributes
{
    float3 positionOS   : POSITION;
    float2 uv           : TEXCOORD0;
    #if defined(STEREO_INSTANCING_ON) || _USE_DRAW_PROCEDURAL
    uint vertexID : SV_VertexID;
    #endif
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct FullScreenVaryings
{
    half4 positionCS    : SV_POSITION;
    half2 uv            : TEXCOORD0;
    UNITY_VERTEX_OUTPUT_STEREO
};

FullScreenVaryings Vertex(FullScreenAttributes input)
{
    FullScreenVaryings output;

    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

    #if VERSION_GREATER_EQUAL(10,0) && defined(STEREO_INSTANCING_ON) || _USE_DRAW_PROCEDURAL
    output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
    output.uv.xy = GetFullScreenTriangleTexCoord(input.vertexID);
    #else
    output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
    output.uv.xy = input.uv;
    #endif
   
    return output;
}

This requires adding

#if VERSION_GREATER_EQUAL(12,0)
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#else
#include "Packages/com.unity.render-pipelines.universal/Shaders/Utils/Fullscreen.hlsl"
#endif

You can actually just use the vertex shaders declared in this library, but it requires adding the following before executing a faux-Blit command. RenderingUtils.Blit also does this and the values are also constants.

cmd.SetGlobalVector("_BlitScaleBiasRt", new Vector4(1,1,0,0));
cmd.SetGlobalVector("_BlitScaleBias", new Vector4(1,1,0,0));

After adopting these changes I’m able to properly do proper blits to the camera’s color target for both eyes, in both 2020.3 and 2021.2. What @daneobyrd mentioned about the TEXTURE2D_X definitely holds true, since render targets are texture arrays when using SPI.

Notes:

  • The this.Blit(cmd, ref renderingData, Material, 0); function that uses a swap buffer, introduced in 2021.2, won’t work. Since it doesn’t use cmd.DrawProcedural for VR.
  • In Unity 2020.3 the shader requires #pragma multi_compile _ _USE_DRAW_PROCEDURAL (now obsolete)
  • For simply copy Blit’s, you can use a material with the
    “Hidden/Universal Render Pipeline/Blit” shader. And use cmd.SetGlobalTexture("
    _SourceTex", ); to set the input/source.
  • 2022.1 introduces a whole new paradigm involving the RTHandles system. Which likely means starting over.

That’s my harvest of the day, good luck! :smile:

4 Likes

Thanks for your very detailed response, very useful and clearly you took time out of your busy day to share. Much appreciated.

I am utterly baffled why Unity doesn’t just make command buffer blit work properly regardless of render pipeline. To force developers to do all this hackery is madness. And who knows what breaks in the next Unity version. Blit works fine in standard pipeline with VR so clearly it’s possible to make it work in URP automatically.

Unity - this is unacceptable, please fix command buffer blit to work in URP and HDRP pipelines in XR/VR without extra code/shader hacks beyond what is normally needed for XR/VR shaders in standard pipeline.

2 Likes

@jjxtra @StaggartCreations
Make sure to read through the thread I linked above for more complaining about Blit (by me) and recent updates and information from someone at Unity.

They are currently doing exactly this. PRs are already up on the graphics repo for replacing all use of cmd.Blit() with the new Blitter API.

1 Like

CoreUtils.SetRenderTarget
ScriptableRenderer.SetRenderTarget
CommandBuffer.SetRenderTarget
Graphics.SetRenderTarget

Graphics.Blit();
CommandBuffer.Blit();
ScriptableRenderPass.Blit();
RenderingUtils.Blit();
Blitter.Blit();

Is this a joke?

8015378--1031852--upload_2022-4-2_22-5-52.png

I support assets for 3 renderings. This is shit. For each postprocessing, I have to guess “which method to use now”.
Now Unity is introducing a new method.
So my code will looks like

#if UNITY_2022.2_OR_ABOVE
Blitter.Blit();
#else
otherRandomBlitMethod
#endif

is any reason why they can’t mark as “deprecated” all old methods “commandBuffer.Blit” and create new methods “commandBuffer.Blit” with the new arguments/logic for srp?

8015378--1031870--upload_2022-4-2_22-11-8.png

3 Likes

I think you misunderstand. The entire issue with CommandBuffer.Blit is that it is a black box. Currently there is no standard for blitting in URP, that is being changed to make things simpler and ultimately less confusing.

2 Likes

I know how “blit” works inside, because I wrote my own universal blit to triangle using RTHandleSystem in standard + urp/hdrp rendering.
So why unity can’t do the same?
All other “blit” methods should be deprecated, and instead of the new “Blitter” class they can just add new methods for command buffers.
Blitter.BlitQuad → commandBuffer.BlitQuad
Blitter.BlitTexture → commandBuffer.BlitTexture
this will avoid the question “what is the current way to use Blit for command buffers”, if there is one left.

Most likely in the future “Blitter” will have problems again and unity will add new class “NewBlitterClass” or something like that

2 Likes

Just do it like Win 32 API, Blitter2, Blitter3, etc. At least the prefix is the same.

1 Like