book/03-urp-architecture.md

03. URP Architecture: Asset / Renderer / Feature / Pass

Summarize URP expansion point (Feature/Pass), life cycle (Create/AddRenderPasses/Setup/Dispose), and Injection Point selection criteria.

This chapter structurally organizes “Where should I put code to expand?” in URP. When writing a Render Feature/Pass, the goal is to have a sense of determining the Injection Point.

3.1 Components of URP

  • UniversalRenderPipelineAsset (URP global settings)
  • ScriptableRenderer (actually configure/run camera rendering)
  • ScriptableRendererFeature (extension point where the user “equips” functionality to the renderer)
  • ScriptableRenderPass (unit of one render pass)

Typically, developers create RendererFeature, attach it to a renderer, create a pass within it, and insert it into the rendering pipeline.

3.1.1 (Important) Feature life cycle: Create / AddRenderPasses / SetupRenderPasses / Dispose

URP calls for ScriptableRendererFeature in approximately the following order:

  1. Create()
    • Called at “configuration change points,” such as when a feature is loaded, toggled active/disabled, or an inspector value changes.
    • In principle, create resources (material/path instance/buffer, etc.) here
  2. AddRenderPasses(renderer, ref RenderingData)
    • Call once per frame, per camera
    • “Register” the pass to the pipeline with renderer.EnqueuePass(pass)
    • Caution: This function is called very frequently, so allocation/creation operations should not be performed.
  3. SetupRenderPasses(renderer, in RenderingData)
    • “Setting” necessary values before executing the pass, including camera target (color/depth)
    • The Unity document also guides you to access camera targets such as cameraColorTarget from SetupRenderPasses, not AddRenderPasses.
  4. Dispose(bool)
    • Resources are released when the feature is destroyed

Rules of practice

  • Create: “Create/initialize (once)”
  • AddRenderPasses: “Determine which camera to apply + Enqueue”
  • SetupRenderPasses: “Camera target/frame-specific parameter settings”
  • Dispose: “Clean up”

Related official documents:

3.2 Where do I insert the pass? (RenderPassEvent)

URP expresses the timing at which the pass will be executed as an event enumeration (e.g. before/after Opaque, before/after Transparent, before/after Post, etc.).

The most common timings in practice:

  • “After Opaque, before Transparent”: Screen-based effects/depth-based effects
  • “Immediately before/immediately after post”: color grading/blur/outline, etc.

In the RenderGraph path, events alone may not be enough, and the actual dependency is determined by “which resource is being read/written”. (RenderGraph is 04. RenderGraph)

3.2.1 Practical criteria for selecting an injection point

When designing a renderer feature, determining the injection point (=pass execution timing) by asking the following questions will reduce mistakes.

  1. What is an input texture?
    • Color that reflects only opaque? Colors that include transparent?
    • Is Depth/Normals necessary?
  2. Where will the output go?
    • Overwrite camera color?
    • Used for temporary RT and then synthesized?
  3. What do subsequent passes expect?
    • Should it be applied before post-processing? Should I apply it later?
  4. Is it safe for XR/Dynamic Resolution/Multiple Cameras?
    • Does each camera use a different target/scale?

The most common mistake is “event-only” timing.
In RenderGraph, resource dependencies (read/write declarations) determine the actual order more strongly than events.

3.2.2 Revisiting injection timing with the Unity 6 frame lifecycle

RenderPassEvent is a hint that determines “when to execute,” and in practice, it is safe to look at the frame stage and input resource preparation status together.| frame steps | General Purpose | Recommended Event (Example) | Common Mistakes | |---|---|---|---| | After Setup/Culling | Check frame input readiness | BeforeRenderingPrePasses Series | Forced on cameras without Depth/Normals | | After Depth Prepass | Depth based effects | AfterRenderingPrePasses | Raw depth linearization missing | | After Opaque | Silhouette/Mask/Distortion Ready | AfterRenderingOpaques | Afterwards, only output is generated without any consumption pass | | After Transparent | Screen distortion/compositing | AfterRenderingTransparents | Missing transparent sorting/sorting assumptions | | Before/After Post | Final Tone/Screen Cover | Before/AfterRenderingPostProcessing | UI/Overlay camera influence range undefined |

3.3 Compatibility Mode vs RenderGraph

There are two ways to create a URP custom pass.

  1. Compatibility Mode: Direct drawing from Execute() to CommandBuffer (legacy approach)
  2. RenderGraph Path: “Declare” paths and resource dependencies in RecordRenderGraph() (current/recommended)

The URP documentation also emphasizes that RenderGraph-based path writing is the focus, and non-RenderGraph paths are no longer actively developed.

3.4 Minimal motion example (skeleton)

Below is a skeleton showing only the structure. An actual RenderGraph pass example is covered in 04. RenderGraph.

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public sealed class ExampleFeature : ScriptableRendererFeature
{
sealed class ExamplePass : ScriptableRenderPass
{
// RenderGraph 경로(권장)
public override void RecordRenderGraph(UnityEngine.Rendering.RenderGraphModule.RenderGraph renderGraph,
ContextContainer frameData)
{
// TODO: renderGraph.AddRasterRenderPass(...) 등으로 패스 선언
}
// Compatibility Mode 경로(레거시)
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
// TODO: CommandBufferPool.Get/Release, Blit, DrawRenderer 등
}
}
ExamplePass _pass;
public override void Create()
{
_pass = new ExamplePass();
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(_pass);
}
}

3.4.1 Application by camera type (required for practice)

URP renders multiple camera types including scene view/game view/preview/reflection. In practice, it is usually applied “only to the game camera” or “scene view is only for debugging”.

public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (renderingData.cameraData.cameraType != CameraType.Game)
return;
renderer.EnqueuePass(_pass);
}

3.5 Designing Pass Structure as ‘Texture Requirements’

The URP pass must declare “this pass requires some textures/input”.

  • If Depth is needed, DepthTexture creation may be necessary.
  • If Normal is needed, the DepthNormals pass may have to be done first.
  • If Motion Vector/History is needed, a separate request arises.

In the RenderGraph path, these requests are structured in the form of UniversalResourceData/Frame Data, and in features such as Full Screen Pass, these are specified as “Requirements.”

Related documents:

3.6 RenderGraph Era Renderer Feature Design Checklist

3.6.1 Resource Readiness/Lifetime

  • Don’t assume Depth/Normals/MotionVectors are always ready for all cameras.
  • Texture format (sRGB/Linear), Render Scale, and DRS should be included in the input contract.
  • If external RT is imported (ImportTexture), document the life subject (graph/external) boundary.

3.6.2 Pass Culling Survivability

RenderGraph can remove a pass if “the output does not lead to the final result.”

  • Ensure that the output handle is actually read in subsequent passes
  • Designed so that even debug passes do not rely solely on side effects (global binding, etc.)
  • The mask creation pass is verified by combining it with the next synthesis pass.

3.6.3 Camera Stack/Multi-Camera Stability

  • Global texture key (SetGlobalTexture) has a high risk of multi-camera collision
  • Separate Base/Overlay camera paths and specify application conditions
  • Safe shutdown with early-out in cameras without input (e.g. depth not ide)

3.7 Sample-based reference notes

We incorporated the concepts from the sample document below into the design criteria for this chapter.

  • samples/Unity6_Rendering_Bible/01_Architecture_and_Pipeline/02_URP_Lifecycle.md
  • samples/Unity6_Rendering_Bible/01_Architecture_and_Pipeline/04_URP_Renderer_Features_Design.md
  • samples/Unity6_Rendering_Bible/04_Render_Graph_System/03_RecordRenderGraph_Migration_Checklist.md

Further reading (official/authoritative sources)