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.
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:
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:
Is the documented code the correct way to do this?
Is the fact ScriptableRenderer.SetRenderTarget is inaccessible a bug?
Is there another way to do make sure the DepthStencil texture is available in the pass?
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.
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.)
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.
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!
@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.
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
I appreciate the heads up, this is exactly the sort of thing that gives me nightmares though
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!
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?
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
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???
// prime the camera and depth buffer, required for URP 13+
CommandBuffer setupCmd = CommandBufferPool.Get();
CoreUtils.SetRenderTarget(setupCmd, camera, depth, ClearFlag.None);
context.ExecuteCommandBuffer(setupCmd);
CommandBufferPool.Release(setupCmd);
CommandBuffer[] cmds = renderingData.cameraData.camera.GetCommandBuffers(cameraEvent);
foreach (CommandBuffer cmd in cmds)
{
context.ExecuteCommandBuffer(cmd);
}
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?
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.
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.
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public class CameraDepthRenderer : ScriptableRendererFeature
{
class CustomRenderPass : ScriptableRenderPass
{
public Material material;
// This method is called before executing the render pass.
// It can be used to configure render targets and their clear state. Also to create temporary render target textures.
// When empty this render pass will render to the active camera render target.
// You should never call CommandBuffer.SetRenderTarget. Instead call <c>ConfigureTarget</c> and <c>ConfigureClear</c>.
// The render pipeline will ensure target setup and clearing happens in a performant manner.
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
{
}
// Cleanup any allocated resources that were created during the execution of this render pass.
public override void OnCameraCleanup(CommandBuffer cmd)
{
}
// Here you can implement the rendering logic.
// Use <c>ScriptableRenderContext</c> to issue drawing commands or execute command buffers
// https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.html
// You don't have to call ScriptableRenderContext.submit, the render pipeline will call it at specific points in the pipeline.
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (material == null) return;
var camera = renderingData.cameraData.camera;
var cmd = CommandBufferPool.Get("RenderDepth");
cmd.Blit(Texture2D.whiteTexture, camera.activeTexture, material);
context.ExecuteCommandBuffer(cmd);
CommandBufferPool.Release(cmd);
}
}
CustomRenderPass m_ScriptablePass;
/// <inheritdoc/>
public override void Create()
{
m_ScriptablePass = new CustomRenderPass();
var shader = Shader.Find("Shader Graphs/CameraRenderDepth");
m_ScriptablePass.material = new Material(shader);
// Configures where the render pass should be injected.
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRendering;
}
// Here you can inject one or multiple render passes in the renderer.
// This method is called when setting up the renderer once per-camera.
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(m_ScriptablePass);
}
public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
{
}
}
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.