Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Resolved ScriptableRenderer.SetRenderTarget is inaccessible in custom RenderPass

Discussion in 'Universal Render Pipeline' started by AurochChris, Nov 3, 2022.

  1. AurochChris

    AurochChris

    Joined:
    Jan 7, 2014
    Posts:
    24
    Hi there,

    Using Unity 2022.1 and URP 13.1.8, we see the following issue:

    We are writing a custom pass that draws renderers to a temporary RT, before blitting the results to the color target. We need the depth texture (Depth + Stencil) during the blit for optimization using stencil culling, and occlusion using per-pixel depth testing.

    In the upgrade guide for URP 13.1.8, the example code shows the usage of ScriptableRenderer.SetRenderTarget during Execute():

    upload_2022-11-3_11-39-8.png


    This same function is used in the URP package's ColorCopyPass.cs. However, the function is marked as internal static, which means the function is call inaccessible from a custom RenderPass added to the project:

    upload_2022-11-3_11-56-49.png

    AFAIK; in order to configure the pass so that the DepthStencil texture is available, you would need to use the SetRendertTarget passing an RTHandle pointing at the camera depth attachment.

    Our questions are:

    1) Is the documented code the correct way to do this?
    2) Is the fact ScriptableRenderer.SetRenderTarget is inaccessible a bug?
    3) Is there another way to do make sure the DepthStencil texture is available in the pass?

    Thanks in advance for all your help. :)
     

    Attached Files:

    Last edited: Nov 3, 2022
    _geo__ likes this.
  2. sandy-unity

    sandy-unity

    Unity Technologies

    Joined:
    Jan 4, 2021
    Posts:
    3
    It seems like a bug and the internal code is there by mistake.
    There is a public API that does the same thing in CoreUtils.SetRenderTarget
    Just make sure you're using the overload that has RTHandle arguments.
     
    AurochChris likes this.
  3. AurochChris

    AurochChris

    Joined:
    Jan 7, 2014
    Posts:
    24
    Thanks for the quick response @sandy-unity . I'll change over the code to use that function.

    If you don't mind the follow up question - since the example code sets the render target during the execute function, I assume it's ok to change your render target during the Execute() function in general?

    For example, if I want to DrawRenderers using one shader pass to one target, and then do a DrawRenderers to using a second shader pass to a second texture, is it advisable to do this in one pass using SetRenderTarget, or should I split this into multiple RenderPasses? (Currently doing the latter.)
     
  4. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    110
    This is currently "ok" as in: it's permitted by the API, and will work. However it is a bad practice, the future improvements to the API we'll release in 23 will make this more restrictive, so if you want to follow best practices and make your code easier to port when upgrading to future versions ideally you should use ConfigureTargets and have a different ScriptableRenderPass per RT switch.

    i.e. instead of:

    PassA {SetRenderTarget(A); DoStuff(); SetRenderTarget(B); DoMoreStuff(); }

    the better way would be:

    PassA.ConfigureTargets(A);
    PassA {DoStuff();}
    PassB.ConfigureTargets(B);
    PassB{ DoMoreStuff(); }

    TLDR: calling SetRenderTarget as many times as you want in Execute() "works", if you want to follow best practices you should use ConfigureTargets
     
  5. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    110
    regarding ScriptableRenderer.SetRenderTarget: if you look at the internal implementation, it just uses the public CoreUtils.SetRenderTarget API (mentioned by @sandy-unity) so you should just be able to reuse 100% of the internal implementation

    We'll look at the upgrade documentation to make sure that it only uses public API, thanks for the catch!
     
    AurochChris likes this.
  6. AurochChris

    AurochChris

    Joined:
    Jan 7, 2014
    Posts:
    24
    @ManueleB Thanks so much for that detailed answer on the best practices - that was exactly what I wanted to understand. I know you have a lot on your plate, but if you were able to fold that into the documentation, I think it'd be very beneficial - there's not a lot of examples out there of a RenderFeature needing multiple passes to work towards a final result.

    I can't believe didn't catch that ScriptableRenderer.SetRenderTarget was just calling the CoreUtils function; as I clearly looked at the source code to look at the access- but it led me to setup multiple render passes as you suggest, so all worked out in the end. :)

    Thanks again!
     
    goncalo-vasconcelos and ManueleB like this.
  7. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    110
    once the new API (that will enforce more strict pass setup) lands (in 23.2) we are planning to write migration and best practices guides so that should help figuring out the optimal way of setting up passes
     
    daneobyrd and AurochChris like this.
  8. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    297
    I appreciate the heads up, this is exactly the sort of thing that gives me nightmares though :D

    Is there any more info you can provide in the meantime? For assets supporting URP the constant API changes are a constant uphill battle (although I do understand they are beneficial and good long term, like the RTHandle changes). The information is really useful for helping us get things prepared before we get inundated with support requests!

    I currently make fairly extensive use of:
    Code (CSharp):
    1. buffer.SetRenderTarget(..., (RenderTargetIdentifier)..._Depth);
    within a single pass, e.g. to prepare buffers and then do some post processing on them. Following your above messages, am I right that the intended usage is to make this more fine-grained? So rather than having one pass that changes targets, I should have multiple passes, where each pass is intended for a specific target? If so, how can I guarantee the ordering of these finer-grained passes with respect to each other, using the current 'EnqueuePass' functionality?
     
    bluescrn likes this.
  9. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    110
    in short: the problem is that a ScriptableRenderPass in URP currently doesn't match an "hardware" RenderPass (think of it at the API level, like Vulkan RenderPass, Metal Encoder, etc). The problem with that is that we are unable to fully optimize and merge passes at the URP level when you start having multiple RT switches inside Execute code, so the downside of this is much less opportunity for URP to optimize the frame. Basically we can never make the assumption that a ScriptableRenderPass will end up being an "actual" pass in the backend.

    In 23.2 we are moving to the RenderGraph API, this is already used by HDRP so it also gets us in a better place in terms of having both pipelines using the same API. Thanks to the new API, every time you will have to switch an RT you will have to create a new RenderGraph pass. This makes URP's life much easier in terms of having the guarantee that every pass will never change bound attachments, resulting in much better end performance, and also clearer API.

    You can have a look at the HDRP code base to have a preview of the API, even though for any proper deep dive I suggest to wait for us to release it in URP. By that time we'll provide more info and upgrade guides
     
  10. jjxtra

    jjxtra

    Joined:
    Aug 30, 2013
    Posts:
    1,467
    Following this migration guide: https://docs.unity.cn/Packages/com....iversal@14.0/manual/upgrade-guide-2022-1.html

    I am getting _CameraColorAttachmentA and other _Camera* errors in the console. What is the secret sauce to migrate from URP 12 to 14 successfully? Why were these property names changed???

    Code (CSharp):
    1.  
    2. // prime the camera and depth buffer, required for URP 13+
    3. CommandBuffer setupCmd = CommandBufferPool.Get();
    4. CoreUtils.SetRenderTarget(setupCmd, camera, depth, ClearFlag.None);
    5. context.ExecuteCommandBuffer(setupCmd);
    6. CommandBufferPool.Release(setupCmd);
    7.  
    8. CommandBuffer[] cmds = renderingData.cameraData.camera.GetCommandBuffers(cameraEvent);
    9. foreach (CommandBuffer cmd in cmds)
    10. {
    11.     context.ExecuteCommandBuffer(cmd);
    12. }
    13.  
     
    Last edited: Jan 4, 2023
    nasos_333 likes this.
  11. bluescrn

    bluescrn

    Joined:
    Feb 25, 2013
    Posts:
    646
    That sounds a bit painful for anybody dealing with postprocessing effects that involve a lot blitting back and forth between render targets, think blur/bloom effects, rendering outlines/glows around objects, etc?
     
    _geo__, ontrigger, jRocket and 2 others like this.
  12. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    297
    I agree with @bluescrn , it sounds a nightmare to change the intention of the SRPass so late in the game
     
  13. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    110
    Once the API is ready we'll share details. It is going to be still possible and pretty simple to add multiple (hardware) passes in a single URP pass like before. Documentation on how to port existing passes will also be provided.
     
  14. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    539
    is there any reason why urp and hdrp teams couldn't agree on a common API 4 years ago?
     
  15. AljoshaD

    AljoshaD

    Unity Technologies

    Joined:
    May 27, 2019
    Posts:
    282
    You can read about that here.
     
  16. clabbe

    clabbe

    Joined:
    Aug 9, 2013
    Posts:
    26
  17. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    539
    In any case, do you have any ETA when the API will be ready?
     
  18. StrangeWays777

    StrangeWays777

    Joined:
    Jul 21, 2018
    Posts:
    33
    URP scriptable renderers are an absolute mess for new comers, every forum I go to people are saying to do different things because things are constantly changing then you check the documentation and it leaves you with more questions than answers. Just a few examples from the latest URP would be helpful but instead new comers get migration tips for outdated versions that have no documentation and have changed drastically from what we have today. It's impossible to learn how to use it atm. Has anybody got an example of how to properly use the new URP scriptable render feature for the love of God? I am losing my mind here, I've been going to-and-fro for the last few days and I'm none the wiser.

    This is what I have so far for a simple Depth Renderer, the material works fine if I place it on an object so I know the material isn't the problem but whenever I enable this renderer feature everything just goes a solid colour, sometimes black and somtimes red.

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. public class CameraDepthRenderer : ScriptableRendererFeature
    6. {
    7.     class CustomRenderPass : ScriptableRenderPass
    8.     {
    9.         public Material material;
    10.  
    11.         // This method is called before executing the render pass.
    12.         // It can be used to configure render targets and their clear state. Also to create temporary render target textures.
    13.         // When empty this render pass will render to the active camera render target.
    14.         // You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
    15.         // The render pipeline will ensure target setup and clearing happens in a performant manner.
    16.         public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    17.         {
    18.         }
    19.  
    20.         // Cleanup any allocated resources that were created during the execution of this render pass.
    21.         public override void OnCameraCleanup(CommandBuffer cmd)
    22.         {
    23.         }
    24.  
    25.         // Here you can implement the rendering logic.
    26.         // Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
    27.         // https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
    28.         // You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
    29.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    30.         {
    31.             if (material == null) return;
    32.  
    33.             var camera = renderingData.cameraData.camera;
    34.             var cmd = CommandBufferPool.Get("RenderDepth");
    35.  
    36.             cmd.Blit(Texture2D.whiteTexture, camera.activeTexture, material);
    37.  
    38.             context.ExecuteCommandBuffer(cmd);
    39.             CommandBufferPool.Release(cmd);
    40.         }
    41.     }
    42.  
    43.     CustomRenderPass m_ScriptablePass;
    44.  
    45.     /// <inheritdoc/>
    46.     public override void Create()
    47.     {
    48.         m_ScriptablePass = new CustomRenderPass();
    49.  
    50.         var shader = Shader.Find("Shader Graphs/CameraRenderDepth");
    51.         m_ScriptablePass.material = new Material(shader);
    52.  
    53.         // Configures where the render pass should be injected.
    54.         m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRendering;
    55.     }
    56.  
    57.     // Here you can inject one or multiple render passes in the renderer.
    58.     // This method is called when setting up the renderer once per-camera.
    59.     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    60.     {
    61.         renderer.EnqueuePass(m_ScriptablePass);
    62.     }
    63.  
    64.     public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
    65.     {
    66.     }
    67. }
    This is my depth render shader used on a quad with my render feature disabled just to show that the material is not the problem.
    Untitled 2.png

    Here it is with my renderer feature enabled which is definately a problem.
    Untitled 3.png
     
    Last edited: Jul 6, 2023
    a52 and goncalo-vasconcelos like this.
  19. AljoshaD

    AljoshaD

    Unity Technologies

    Joined:
    May 27, 2019
    Posts:
    282