Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Bug Fullscreen blit raises a Null Reference Exception when using Blitter API on a 2D project

Discussion in 'Universal Render Pipeline' started by ElliotB, Jun 25, 2023.

  1. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    215
    A null reference exception is raised every time when using the Blitter API to copy from one render target to another in Universal Render Pipeline when a 2D render pipeline asset is in use.
    (This is when using blit without specifying a material override - it appears one of the core materials is not being correctly created for 2D projects).

    Below is a minimal test case illustrating the bug. Note that this code is reproducing almost verbatim an example from the URP documentation:

    https://docs.unity3d.com/Packages/c...renderer-features/how-to-fullscreen-blit.html

    2022.3.1f1

    SomeBlitRendererFeature
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering.Universal;
    3.  
    4. internal class SomeBlitRendererFeature : ScriptableRendererFeature
    5. {
    6.     public Shader m_Shader;
    7.     public float m_Intensity;
    8.  
    9.     SomeBlitPass m_RenderPass = null;
    10.  
    11.     public override void AddRenderPasses(ScriptableRenderer renderer,
    12.                                     ref RenderingData renderingData)
    13.     {
    14.         if (renderingData.cameraData.cameraType == CameraType.Game)
    15.             renderer.EnqueuePass(m_RenderPass);
    16.     }
    17.  
    18.     public override void SetupRenderPasses(ScriptableRenderer renderer,
    19.                                         in RenderingData renderingData)
    20.     {
    21.         if (renderingData.cameraData.cameraType == CameraType.Game)
    22.         {
    23.             // Calling ConfigureInput with the ScriptableRenderPassInput.Color argument
    24.             // ensures that the opaque texture is available to the Render Pass.
    25.             m_RenderPass.ConfigureInput(ScriptableRenderPassInput.Color);
    26.             m_RenderPass.SetTarget(renderer.cameraColorTargetHandle);
    27.         }
    28.     }
    29.  
    30.     public override void Create()
    31.     {
    32.         m_RenderPass = new SomeBlitPass();
    33.     }
    34.  
    35.     protected override void Dispose(bool disposing)
    36.     {
    37.     }
    38. }
    SomeBlitPass
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. internal class SomeBlitPass : ScriptableRenderPass
    6. {
    7.     ProfilingSampler m_ProfilingSampler = new ProfilingSampler("TestingBlit");
    8.     RTHandle m_CameraColorTarget;
    9.  
    10.     public SomeBlitPass()
    11.     {
    12.         renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
    13.     }
    14.  
    15.     public void SetTarget(RTHandle colorHandle)
    16.     {
    17.         m_CameraColorTarget = colorHandle;
    18.     }
    19.  
    20.     public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    21.     {
    22.         ConfigureTarget(m_CameraColorTarget);
    23.     }
    24.  
    25.     public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    26.     {
    27.         var cameraData = renderingData.cameraData;
    28.         if (cameraData.camera.cameraType != CameraType.Game)
    29.             return;
    30.  
    31.         CommandBuffer cmd = CommandBufferPool.Get();
    32.         using (new ProfilingScope(cmd, m_ProfilingSampler))
    33.         {
    34.             Blitter.BlitCameraTexture(cmd, m_CameraColorTarget, m_CameraColorTarget);
    35.         }
    36.         context.ExecuteCommandBuffer(cmd);
    37.         cmd.Clear();
    38.  
    39.         CommandBufferPool.Release(cmd);
    40.     }
    41. }
    Add the feature to a 2D renderer and it will throw the null reference exception:
    Code (csharp):
    1. ArgumentNullException: Value cannot be null.
    2. Parameter name: material
    The erroneous code in URP comes from the call to BlitTexture, which invokes GetBlitMaterial(TextureXR.dimension) and gets a null return.
    Code (CSharp):
    1.  
    2. BlitTexture(cmd, source, scaleBias, GetBlitMaterial(TextureXR.dimension), bilinear ? 1 : 0);
    3.  
     
    Last edited: Jun 25, 2023
  2. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    215
    Reported as IN-45287
     
  3. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    98
    can you check in the frame debugger that you are not trying to bind the backbuffer as a texture? The main difference between this code and the URP tutorial you linked, is that the tutorial is using a custom material, that samples the _CameraOpaqueTexture, in your case since you assign no material you are trying to bind as texture the source RT parameter of BlitCameraTexture: in this case there is 2 potential errors in the code you posted

    • having source == target will cause the underlying code to try bind as texture the current attachment (active color RT)
    • if m_CameraColorTarget is the backbuffer, you cannot bind that as a texture because it's not possible on any gfx API (I suspect the null reference error might be caused by this)
     
  4. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    98
    in general if you are trying to read from a texture as input, and then rewrite the results to the same texture, the correct pattern of doing this is:

    Blit(source, tempRT);
    Blit(tempRT, source);

    Also making sure that the source texture is not the backbuffer (swapchain surface) but an offscreen RenderTexture (for example the camera opaque texture, which is just a copy of the current active color target, which is safe to bind to shaders)
     
  5. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    215
    Hi ManueleB,

    Thank you for your reply, more details below.

    It's not possible to check the frame debugger with this, as Unity fails to even draw the frame debugger UI:

    upload_2023-6-27_8-54-0.png

    I was aware of this last bit and my original code where I found this bug does not do this, I wanted to make it as close as possible to the example in the documentation for simplicity. Fair point though! I've updated the example code to blit from screen to a temporary buffer and back, but the same error still occurs.

    I'm surprised by this, there's no mention of these requirements in the documentation and it works absolutely fine on 3D URP since about version 7 or so. What method are we supposed to use to get the camera target? There's even renderingData.cameraData.renderer.cameraColorTargetHandle exposed, which was updated when the RTHandle API went live - maybe I am misunderstanding you?

    Cheers!

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. internal class SomeBlitPass : ScriptableRenderPass
    6. {
    7.     ProfilingSampler m_ProfilingSampler = new ProfilingSampler("TestingBlit");
    8.     RTHandle m_CameraColorTarget;
    9.     RTHandle _temp;
    10.  
    11.     public SomeBlitPass()
    12.     {
    13.         renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
    14.     }
    15.  
    16.     public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    17.     {
    18.         RenderingUtils.ReAllocateIfNeeded(ref _temp, cameraTextureDescriptor, name: "blit_test_target");
    19.     }
    20.  
    21.     public void SetTarget(RTHandle colorHandle)
    22.     {
    23.         m_CameraColorTarget = colorHandle;
    24.     }
    25.  
    26.     public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    27.     {
    28.         ConfigureTarget(m_CameraColorTarget);
    29.     }
    30.  
    31.     public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    32.     {
    33.         var cameraData = renderingData.cameraData;
    34.         if (cameraData.camera.cameraType != CameraType.Game)
    35.             return;
    36.  
    37.         CommandBuffer cmd = CommandBufferPool.Get();
    38.         using (new ProfilingScope(cmd, m_ProfilingSampler))
    39.         {
    40.             Blitter.BlitCameraTexture(cmd, m_CameraColorTarget, _temp);
    41.             Blitter.BlitCameraTexture(cmd, _temp, m_CameraColorTarget);
    42.         }
    43.         context.ExecuteCommandBuffer(cmd);
    44.         cmd.Clear();
    45.  
    46.         CommandBufferPool.Release(cmd);
    47.     }
    48.  
    49.     public void Dispose()
    50.     {
    51.         _temp?.Release();
    52.     }
    53. }

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering.Universal;
    3.  
    4. internal class SomeBlitRendererFeature : ScriptableRendererFeature
    5. {
    6.     public Shader m_Shader;
    7.     public float m_Intensity;
    8.  
    9.     SomeBlitPass m_RenderPass = null;
    10.  
    11.     public override void AddRenderPasses(ScriptableRenderer renderer,
    12.                                     ref RenderingData renderingData)
    13.     {
    14.         if (renderingData.cameraData.cameraType == CameraType.Game)
    15.             renderer.EnqueuePass(m_RenderPass);
    16.     }
    17.  
    18.     public override void SetupRenderPasses(ScriptableRenderer renderer,
    19.                                         in RenderingData renderingData)
    20.     {
    21.         if (renderingData.cameraData.cameraType == CameraType.Game)
    22.         {
    23.             // Calling ConfigureInput with the ScriptableRenderPassInput.Color argument
    24.             // ensures that the opaque texture is available to the Render Pass.
    25.             m_RenderPass.ConfigureInput(ScriptableRenderPassInput.Color);
    26.             m_RenderPass.SetTarget(renderer.cameraColorTargetHandle);
    27.         }
    28.     }
    29.  
    30.     public override void Create()
    31.     {
    32.         m_RenderPass = new SomeBlitPass();
    33.     }
    34.  
    35.     protected override void Dispose(bool disposing)
    36.     {
    37.         base.Dispose(disposing);
    38.         if (disposing)
    39.         {
    40.             m_RenderPass.Dispose();
    41.         }
    42.     }
    43. }
     
  6. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    215
    Anyway, the camera target bit is unrelated to the original bug - the blit error still occurs if I attempt to blit between two temporary targets:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. internal class SomeBlitPass : ScriptableRenderPass
    6. {
    7.     ProfilingSampler m_ProfilingSampler = new ProfilingSampler("TestingBlit");
    8.     RTHandle _temp;
    9.     RTHandle _temp2;
    10.  
    11.     public SomeBlitPass()
    12.     {
    13.         renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
    14.     }
    15.  
    16.     public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    17.     {
    18.         RenderingUtils.ReAllocateIfNeeded(ref _temp, cameraTextureDescriptor, name: "blit_test_target");
    19.         RenderingUtils.ReAllocateIfNeeded(ref _temp2, cameraTextureDescriptor, name: "blit_test_target2");
    20.     }
    21.  
    22.     public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    23.     {
    24.         var cameraData = renderingData.cameraData;
    25.         if (cameraData.camera.cameraType != CameraType.Game)
    26.             return;
    27.  
    28.         CommandBuffer cmd = CommandBufferPool.Get();
    29.         using (new ProfilingScope(cmd, m_ProfilingSampler))
    30.         {
    31.             Blitter.BlitCameraTexture(cmd, _temp, _temp2);
    32.         }
    33.         context.ExecuteCommandBuffer(cmd);
    34.         cmd.Clear();
    35.  
    36.         CommandBufferPool.Release(cmd);
    37.     }
    38.  
    39.     public void Dispose()
    40.     {
    41.         _temp?.Release();
    42.         _temp2.Release();
    43.     }
    44. }
     
  7. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    98
    Hey! I think I missed the part where the error was about the material being null :)

    In that case I think the problem is that the 2D renderer is not initializing the Blitter API properly, so the 2D renderer might not have Blitter support yet? I need to check with the 2D team as I am not sure myself, but I don't see the code that initializes it so I suspect that s the case.

    i.e., URP initializes it in UniversalRenderPipelineAsset:


    Code (CSharp):
    1. foreach (var data in m_RendererDataList)
    2.             {
    3.                 if (data is UniversalRendererData universalData)
    4.                 {
    5.                     Blitter.Initialize(universalData.shaders.coreBlitPS, universalData.shaders.coreBlitColorAndDepthPS);
    6.                     break;
    7.                 }
    8.             }
    The workaround in your case would be to specify explicitly a material I suppose?
     
  8. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    98
    regarding this, just to clarify even if unrelated to this specific issue.

    Yes renderingData.cameraData.renderer.cameraColorTargetHandle is the right way to access the current main color target, depending on the frame setup and where your code is injected, this can either be an offscreen color texture or the backbuffer (the swapchain surface). In case you are using this as a source texture it's important to make sure it's not the swapchain surface because it won't be possible to bind it as a texture.

    The backbuffer RTID is BuiltinRenderTextureType.CameraTarget so you can check against that
     
  9. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    215
    Thanks for your reply, that was my guess aswell. Now that the old Blit functionality has been removed though it means the replacement doesn't cover all the previous use cases until fixed, which is unfortunate!

    The workaround to create and initialise my own blit material works but that removes many of the advantages of having the API in the first place. Do you know if this will be accepted on the tracker?

    Cheers
     
  10. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    98
    yeah, apparently it's a known 2D regression and they are working on a fix
     
    ElliotB likes this.
  11. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    215
    Hi Manuelle,

    The associated bug report IN-45287 was just closed as 'Not Qualified' by the QA Team. Is a fix still in progress?

    Cheers,
    Elliot
     
  12. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    98
    I am not sure about your bug specifically but the 2D team was already aware of this issue so they are working on a fix
     
    ElliotB likes this.
  13. noeski

    noeski

    Joined:
    Nov 10, 2015
    Posts:
    3
    @ManueleB any updates on this? I am also running into this issue which prevents me from updating Unity to the latest LTS version.
     
  14. kennyy_

    kennyy_

    Unity Technologies

    Joined:
    Apr 7, 2021
    Posts:
    94
    ManueleB and ElliotB like this.
  15. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    215