04. RenderGraph core concepts and API (based on URP)
The goal of this chapter is to learn “How to create a pass in RenderGraph and how to exchange textures/buffers?” based on actual code.
Preceding: 02. 프레임 루프/데이터 흐름, 03. URP 아키텍처
4.1 RenderGraph’s core philosophy: “Declare dependencies”
Rather than “executing the command right away,” RenderGraph records Pass + Resource Read/Write first.
-Pass A writes texture T -Pass B reads texture T
This declaration determines the execution order and automatically creates/releases/reuses resources as needed.
4.1.1 Understanding Record → Compile → Execute separately
Unity 6 RenderGraph handles one frame in three steps:
- Record: Declare a path and resource read/write contract
- Compile: View entire graph dependencies and optimize path culling/resource alias/barrier
- Execute: Record/execute compiled execution plan as GPU command
With this model in mind, you can quickly narrow down phenomena like “the code is there but the path isn’t working” to a dependency/consumption path issue**.
4.2 URP’s RecordRenderGraph entry point
In URP, RenderGraph-based custom passes are created by overriding ScriptableRenderPass.RecordRenderGraph(RenderGraph, ContextContainer).
At this time, frameData contains URP internal data such as camera/resource/rendering settings in container form.
4.3 Most common pass type: Raster Pass
Most custom passes in URP are “raster passes”.
- Set the render target (color/depth)
- Draw full screen triangles or a specific renderer
- Perform blit (copy/filter).
4.4 Example: Applying a full-screen material to a camera color texture
The code below shows a typical pattern of reading “camera color” and writing the result to the same target. (The exact type/name may vary slightly depending on the URP package version.)
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering.RenderGraphModule;
sealed class FullscreenMaterialPass : ScriptableRenderPass
{
public Material material;
struct PassData
{
public TextureHandle source;
public TextureHandle destination;
public Material material;
}
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
// URP가 제공하는 리소스 데이터(예: 카메라 컬러)
var resources = frameData.Get<UniversalResourceData>();
// 원본(카메라 컬러)
var cameraColor = resources.activeColorTexture;
// 목적지(임시 텍스처를 만들어서 쓴 뒤, URP 리소스를 교체하는 패턴이 안전함)
// 여기서는 개념만: 실제로는 RenderGraphTextureDesc 설정 필요
var desc = renderGraph.GetTextureDesc(cameraColor);
desc.name = "FullscreenMaterial_Temp";
var temp = renderGraph.CreateTexture(desc);
using (var builder = renderGraph.AddRasterRenderPass<PassData>("FullscreenMaterial", out var passData))
{
passData.source = cameraColor;
passData.destination = temp;
passData.material = material;
builder.UseTexture(passData.source, AccessFlags.Read);
builder.SetRenderAttachment(passData.destination, 0, AccessFlags.Write);
builder.SetRenderFunc(static (PassData data, RasterGraphContext ctx) =>
{
// URP 제공 유틸(버전에 따라 Blitter/RenderingUtils 등을 사용)
Blitter.BlitTexture(ctx.cmd, data.source, new Vector4(1, 1, 0, 0), data.material, 0);
});
}
// URP의 “activeColorTexture”를 temp로 갱신하는 패턴(버전에 따라 API가 다름)
resources.activeColorTexture = temp;
}
}
Key points
- Reading texture is declared as
UseTexture(..., Read) - Render target to be used is declared as
SetRenderAttachment(..., Write) - To continue writing results in the URP pipeline, you need to update “Which resources should subsequent passes see”.
4.4.1 SetRenderFunc defaults to static lambda.
Capturing external variables in SetRenderFunc may result in closure allocations per frame.
If possible, the pattern of filling in all required values for passData and fixing the executable code with static lambda is safe.
4.5 ImportTexture: Connect external (RenderTexture/RTHandle) resources to the graph.
RenderGraph principally manages the resources produced by the graph. However, there are many cases where you need to “import” an already existing RTHandle or external texture into the graph.
The URP documentation has an example of getting an RTHandle as a TextureHandle with renderGraph.ImportTexture(rtHandle).
4.6 “Pass is a contract, not a function call”: Read/write declaration rules
The biggest change in mindset when moving to RenderGraph is this.
- Compatibility Mode: It’s all about “what I wrote in cmd”
- RenderGraph: You need to declare “what resources my pass reads/writes” for the graph to be constructed correctly.
4.6.1 Commonly incorrect declarations
- Not declaring
UseTexture(Read)while sampling texture - Depth texture is used but depth attachment is not set.
- Specify the same texture as source/destination simultaneously (especially Blit)
Because RenderGraph relies on “declaration” for dependency/barrier/lifetime management, If the declaration is incorrect, problems such as black screen/zero sampling/flicker/platform differences may occur.
4.7 Frame Data Access: UniversalCameraData / UniversalResourceData
In the URP RenderGraph path, “camera-related data” and “resource (texture) data” are provided as Frame Data.
Core types (based on URP document):
UniversalCameraData: Camera information (view/projection, camera type, etc.)UniversalResourceData: Texture resources such as camera color/depth/history
The pattern of accessing ContextContainer to frameData.Get<T>() is the basis for all custom passes.
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
{
var cameraData = frameData.Get<UniversalCameraData>();
var resources = frameData.Get<UniversalResourceData>();
// 예시(정확한 필드명은 URP 버전에 따라 다를 수 있음)
var color = resources.activeColorTexture;
}
Related official documents:
- Frame Data Access: https://docs.unity3d.com/kr/6000.3/Manual/urp/accessing-frame-data.html
4.8 History Render Textures: The basis for temporal-based effects
Effects like TAA, motion blur, and time-stacked upscaler require textures from the “previous frame”.
URP provides documentation covering historical textures in the RenderGraph path.
- Key point: History textures are “camera-specific” and reset conditions (camera cuts/resolution changes, etc.) must be taken into account.
Related official documents:
- History Render Textures: https://docs.unity3d.com/Manual/urp/render-graph-history-render-textures.html
4.9 Handle Blit/Copy “explicitly” in RenderGraph
The URP documentation separately explains how to perform blit in RenderGraph and how to optimize it effectively (avoiding unnecessary copies).
- Principle: “Blit only when really necessary”
- Reason: Copy/Blit eats bandwidth and is expensive, especially on mobile/tile-based GPUs.
Related official documents:
- Blit in RenderGraph: https://docs.unity3d.com/Manual/urp/render-graph-blit.html
- Blit optimization: https://docs.unity3d.com/Manual/urp/blit-best-practices.html
4.10 Pass Culling: Common causes and solutions for “my pass disappearing”
One of the normal behaviors of RenderGraph is pass culling. If the output does not contribute to the final result, the pass is removed.
Representative causes:
- No one reads the output texture
- Changed the camera color but did not consume the replacement copy afterwards
- Only global binding side effects and no consumption path in the graph.
Response Checklist:
- Make sure the output handle is connected to the next pass input
- Temporarily verify survivability by attaching a consumption pass even if it is a debug pass.
- Check pass existence and resource lifetime together in RenderGraph Viewer
4.11 Legacy Execute → RecordRenderGraph Previous checklist
When migrating, fixing the 5 questions below first will reduce your chances of failure.
- What is an input resource? (
Color/Depth/Normals/MotionVectors/History) - What are output resources? (Camera color replacement/separate mask/buffer)
- Are there any side effects? (
SetGlobalTexture, writing external buffer, etc.) - Are there any conditional execution rules? (camera type/platform/quality grade)
- Is synchronization with other passes necessary? (Flow of reading compute results from Raster)
What not to do:
- Attempt to call GPU immediately at step
RecordRenderGraph GetTemporaryRT/ReleaseTemporaryRTImport the pattern as is- Share global state without camera scope
4.12 Sample-based reinforcement points
This chapter has been enhanced by incorporating practical points from the sample documents below.
samples/Unity6_Rendering_Bible/04_Render_Graph_System/01_RenderGraph_Basics.mdsamples/Unity6_Rendering_Bible/04_Render_Graph_System/02_Writing_Custom_Pass.mdsamples/Unity6_Rendering_Bible/04_Render_Graph_System/03_RecordRenderGraph_Migration_Checklist.mdsamples/Unity6_Rendering_Bible/04_Render_Graph_System/04_RenderGraph_Debugging_and_Culling.md
Further reading (official/authoritative sources)
- Write a RenderGraph pass (overview): https://docs.unity3d.com/Manual/urp/render-graph-write-render-pass.html
- RenderGraph ImportTexture: https://docs.unity3d.com/Manual/urp/render-graph-import-texture.html