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.

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

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.

C#
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”.

C#
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 generated)

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)