[ALINE/URP] Lower priority camera causes draws to higher priority camera to be invisible

First I just wanted to say this is such a marvelous library! Thank you for making it :clap:

So, in my project, I have a setup where I draw ALINE primitives to a separate off-main-screen area which will be picked up by a second camera that acts as an overlay. This overlay camera is set to a higher depth/priority and uses the DontClear/Uninitialized background mode so that the base camera output will be rendered underneath. I do it this way so that postprocessing shaders can be independent of the ALINE draws.

I had this working before in the Unity built-in renderer, and after transitioning to URP, the overlay draws from ALINE no longer appear in the game view (although they do show in the scene view). I think this might be a bug in ALINE because (a) other game objects respect the camera priority as expected and (b) when the base camera is disabled so only the overlay camera is active, the ALINE draws are visible again.

This may be related to this old bug report here:

That bug report was missing the requested minimal repro, so I’ve created one here in Unity 2021.3.17f1 with ALINE 1.6.4:
https://www.dropbox.com/s/izqwx48nlioinwa/ALINE-2DURP-MultiCamera.zip?dl=0

The repro is very simple: use the Unity 2D URP template, create two cameras, one with a higher priority and background mode set to Uninitialized, and make an ALINE draw to the view of the camera with the higher priority.

Interestingly, even if the background color of the overlay is set to solid color, the ALINE draws are still invisible. Somehow the presence of the other camera, despite its lower priority interferes with the rendering process.

Here is what the minimal repro scene shows:

Screen Shot 2023-01-25 at 6.25.18 PM

There should be corresponding text “Overlay” in green.

I tracked down the issue through debugging the call stack of the ALINEURPRenderPass. The render passes for each camera are being enqueued at the beginning of the frame render, which means that the first camera in the render stack (i.e. the one with the lowest depth/priority) dequeues all the render passes. So instead of each camera receiving one render pass each, the first camera receives all the render passes.

Specifically, in DrawingManager.cs the passes were being enqueued in a beginFrameRendering callback instead of beginCameraRendering callback. Here is the minimal fix I used:

diff --git a/Assets/ThirdParty/ALINE/DrawingManager.cs b/Assets/ThirdParty/ALINE/DrawingManager.cs
index 1dc8264..1e4d90f 100644
--- a/Assets/ThirdParty/ALINE/DrawingManager.cs
+++ b/Assets/ThirdParty/ALINE/DrawingManager.cs
@@ -259,6 +259,7 @@ namespace Drawing {
 			Camera.onPostRender += PostRender;
 			// Callback when rendering with a scriptable render pipeline
 			UnityEngine.Rendering.RenderPipelineManager.beginFrameRendering += BeginFrameRendering;
+			UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering += BeginCameraRendering;
 			UnityEngine.Rendering.RenderPipelineManager.endCameraRendering += EndCameraRendering;
 #if UNITY_EDITOR
 			EditorApplication.update += OnUpdate;
@@ -268,19 +269,18 @@ namespace Drawing {
 
 		void BeginFrameRendering (ScriptableRenderContext context, Camera[] cameras) {
 			RefreshRenderPipelineMode();
+		}
 
+		void BeginCameraRendering (ScriptableRenderContext context, Camera camera) {
 #if MODULE_RENDER_PIPELINES_UNIVERSAL
 			if (detectedRenderPipeline == DetectedRenderPipeline.URP) {
-				for (int i = 0; i < cameras.Length; i++) {
-					var cam = cameras[i];
-					var data = cam.GetUniversalAdditionalCameraData();
-					if (data != null) {
-						var renderer = data.scriptableRenderer;
-						if (renderPassFeature == null) {
-							renderPassFeature = ScriptableObject.CreateInstance<AlineURPRenderPassFeature>();
-						}
-						renderPassFeature.AddRenderPasses(renderer);
+				var data = camera.GetUniversalAdditionalCameraData();
+				if (data != null) {
+					var renderer = data.scriptableRenderer;
+					if (renderPassFeature == null) {
+						renderPassFeature = ScriptableObject.CreateInstance<AlineURPRenderPassFeature>();
 					}
+					renderPassFeature.AddRenderPasses(renderer);
 				}
 			}
 #endif
@@ -291,6 +291,7 @@ namespace Drawing {
 			actuallyEnabled = false;
 			Camera.onPostRender -= PostRender;
 			UnityEngine.Rendering.RenderPipelineManager.beginFrameRendering -= BeginFrameRendering;
+			UnityEngine.Rendering.RenderPipelineManager.beginCameraRendering -= BeginCameraRendering;
 			UnityEngine.Rendering.RenderPipelineManager.endCameraRendering -= EndCameraRendering;
 #if UNITY_EDITOR
 			EditorApplication.update -= OnUpdate;

In this fix, I left the RefreshRenderPipelineMode() in the BeginFrameRendering callback so that it only gets called once per frame, rather than remove the frame rendering callback entirely.

2 Likes

Nice, I was struggling with drawing labels to my Overlay camera. This fix works perfectly!

1 Like

Thanks! I’ll include this fix in the next update :slight_smile: (better late than never)

1 Like

Glad to hear the fix still holds up!

Thank you for adding the fix to the next update! Very glad to see this issue resolved :blush: