05. Texture resources and “ID” structure: RenderTexture / RTHandle / TextureHandle
This chapter explains “how to exchange render textures” and what exactly the commonly referred to “reference ID” is.
Precedence: 04. RenderGraph
5.1 Glossary of terms
RenderTexture: Unity engine level texture resource (render target possible)RTHandle: Render target handle considering dynamic resolution/scale (common to URP/HDRP)TextureHandle: Handle for RenderGraph to track resources (internal representation of the graph)- “Shader Property ID”: Global property slot identifier such as
Shader.PropertyToID("_MyTex")
Why mixing up “IDs” causes confusion
- Global slot ID (integer): Used as a key when setting shader global texture/constants
- Resource Handle (RTHandle/TextureHandle): Actual GPU resource (or wrapping it)
- RenderTexture instance: C# object reference
In practice, these three must be connected according to the situation.
5.2 Global Texture Slot: Shader.PropertyToID
The oldest (and still common) pattern is:
_MyColorTexfor the same nameint id = Shader.PropertyToID("_MyColorTex")cmd.SetGlobalTexture(id, rt)orcmd.SetGlobalTexture("_MyColorTex", rt)
Here id is just a “slot key with name hashed to an integer” and not the texture itself.
5.2.1 Reasons and pitfalls of using “ID slot”
- Reason: Reduce string comparison costs and speed up property access
- Pitfall: If you mistakenly believe that “ID is a resource,” your design will suffer.
The ID is just the result of the (name → slot) transformation.
- Resources are
RenderTexture/RTHandle/TextureHandle - The slot is
intorstring
, and a call like cmd.SetGlobalTexture connects the two.
5.3 Basic pattern for exchanging RenderTexture (Compatibility Mode)
The simplest way is:
- Create a temporary render target (
GetTemporaryRTor allocate RTHandle) - Render in any pass (Blit/Draw)
- Bind to global slot (
SetGlobalTexture) - Sample from another pass/shader
static readonly int MyTexId = Shader.PropertyToID("_MyTex");
var cmd = CommandBufferPool.Get("Example");
try
{
cmd.GetTemporaryRT(MyTexId, width, height, 0, FilterMode.Bilinear, RenderTextureFormat.ARGBHalf);
cmd.SetRenderTarget(MyTexId);
// ... draw ...
cmd.SetGlobalTexture(MyTexId, MyTexId);
context.ExecuteCommandBuffer(cmd);
}
finally
{
cmd.ReleaseTemporaryRT(MyTexId);
CommandBufferPool.Release(cmd);
}
The above example is for “concept” explanation only. In practice, it is recommended to move on to RTHandle/RenderGraph.
5.3 RTHandle: Why do you need it?
When dealing with dynamic resolutions (Scale, XR, different sizes for each camera), carrying RenderTexture directly can easily create leakage/reallocation issues.
RTHandleis:
- Scale/dynamic resolution response
- Inter-frame reuse and lifespan management
- Used with URP/HDRP common utility
This is the “handle system” introduced for this purpose.
5.3.1 RTHandle and resolution scale (dynamic resolution)
When performing Forward+/Post/Upscaling, the camera target is:
- May differ from screen size
- Apply render scale
- May vary from eye to eye in XR.
At this time, if you directly create/manage RenderTexture with “exact pixel size,” errors will easily occur.
RTHandle is a method of managing “reference (handle) + actual texture (backing)” separately for these cases.
5.4 TextureHandle: “Resource Reference” inside RenderGraph
RenderGraph internally references texture resources as TextureHandle.
- The texture created by the graph is referenced by the handle obtained by
CreateTexture() - External RTHandle/Texture is imported and referenced as a handle by
ImportTexture()
The key is to declare “resource dependency (read/write)” in handle units.
5.5 Basic pattern for exchanging textures in RenderGraph (recommended)
In RenderGraph it usually goes one of the following:
- Read the camera texture provided by URP (
UniversalResourceData.activeColorTexture, etc.) - Create a temporary texture inside the graph (
CreateTexture) - Declare read/write in path (
UseTexture/SetRenderAttachment) - Update resources that subsequent passes will reference (e.g. replace activeColorTexture with temp)
Important differences:
- Compatibility Mode makes it easy for “global slots” to become the center of flow.
- In RenderGraph, “handles (dependencies)” are the center of the flow.
5.5 3 types of exchange method patterns (summary)
-
(Legacy) CommandBuffer + GlobalTexture slot
- Pros: Simple, many examples
- Disadvantage: resource lifetime/dependencies scattered throughout the code (difficult to debug/optimize)
-
RTHandle-based (URP internal style)
- Renderer/Pass maintains resources around RTHandle
-
RenderGraph(TextureHandle) based (current/recommended)
- Pass declares read/write through handle
- Graphs help with lifespan/reuse
5.6 How to answer “Where does camera color/depth texture come from?”
First thing to do in the RenderGraph route:
- Read the Frame Data document (concept),
- View the actual graph (phenomenon) with RenderGraph Viewer,
- Try changing URP settings (Requirements/Depth Texture/Normal Texture) (cause).
Related official documents:
- Frame Data Access: https://docs.unity3d.com/kr/6000.3/Manual/urp/accessing-frame-data.html
- RenderGraph Viewer: https://docs.unity3d.com/Manual/urp/render-graph-view.html
Further reading (official/authoritative sources)
- Shader.PropertyToID: https://docs.unity3d.com/ScriptReference/Shader.PropertyToID.html
- RTHandle: https://docs.unity3d.com/Manual/urp/render-graph-rt-handle.html