第11章 URP渲染管线架构

第11章 URP渲染管线架构

理论讲解

11.1 UniversalRenderPipeline类解析

UniversalRenderPipeline (URP) 是Unity的可编程渲染管线实现,专为跨平台性能优化而设计。它是Scriptable Render Pipeline (SRP) 框架的一部分,提供了灵活的渲染管线定制能力。

UniversalRenderPipeline类的主要职责:

  1. 渲染管线管理:作为渲染管线的入口点,管理整个渲染流程
  2. 资源管理:管理渲染过程中使用的临时资源和缓存
  3. 相机处理:处理场景中的所有相机及其渲染需求
  4. 渲染状态管理:维护渲染管线的状态和配置

URP核心架构组件

RenderPipelineAsset:渲染管线的配置数据,定义管线的基本设置

  • 包含渲染管线的全局设置
  • 定义默认的Renderer配置
  • 存储渲染管线的元数据

RenderPipeline:渲染管线的运行时实例

  • 实现IRenderPipeline接口
  • 处理实际的渲染调用
  • 管理渲染资源的生命周期

ScriptableRenderer:可编程渲染器,定义渲染逻辑

  • 处理相机的渲染请求
  • 管理渲染特性(Renderer Features)
  • 执行渲染通道(Render Passes)

11.2 RenderPipelineAsset与RenderPipeline关系

RenderPipelineAsset和RenderPipeline是URP架构中的两个核心概念,它们之间存在紧密的关系:

RenderPipelineAsset

  • 配置容器:存储渲染管线的配置数据
  • 持久化:保存在项目中,可序列化到磁盘
  • 编辑器集成:在Unity编辑器中可编辑和配置
  • 实例化:运行时用于创建RenderPipeline实例

RenderPipeline

  • 运行时实例:基于Asset创建的运行时对象
  • 渲染执行:实际执行渲染逻辑
  • 资源管理:管理渲染过程中的资源
  • 生命周期:与场景生命周期相关

11.3 ScriptableRenderer架构设计

ScriptableRenderer是URP中负责实际渲染逻辑的核心组件,它提供了完全可定制的渲染流程。

ScriptableRenderer主要功能

  1. 渲染通道管理:管理一系列渲染通道的执行顺序
  2. 相机数据处理:处理相机的渲染数据和配置
  3. 渲染特性集成:集成各种渲染特性(如后处理、阴影等)
  4. 资源分配:管理渲染过程中使用的临时资源

ScriptableRenderer生命周期

  • 创建:从Renderer Asset实例化
  • 配置:根据相机数据配置渲染器
  • 执行:执行渲染通道
  • 清理:释放渲染资源

11.4 RenderPass系统设计模式

RenderPass是URP中最小的渲染单元,每个RenderPass负责特定的渲染任务。

RenderPass类型

  1. ScriptableRenderPass:基础渲染通道基类
  2. ScriptableRenderer:管理多个RenderPass
  3. 自定义RenderPass:开发者可创建的特定渲染任务

RenderPass执行流程

  1. 配置阶段:设置渲染目标和渲染状态
  2. 执行阶段:执行实际的渲染命令
  3. 清理阶段:释放渲染资源

11.5 CommandBuffer与渲染命令

CommandBuffer是Unity中用于记录渲染命令的数据结构,在URP中广泛使用。

CommandBuffer在URP中的应用

  • 渲染命令记录:记录GPU渲染命令
  • 多Pass协调:在不同渲染通道间传递数据
  • 性能优化:批量执行渲染命令

CommandBuffer管理

  • 创建与销毁:正确管理CommandBuffer生命周期
  • 命令记录:记录渲染命令到缓冲区
  • 执行与同步:执行命令并处理同步问题

11.6 渲染流程整体概览

URP的渲染流程遵循以下主要步骤:

渲染流程步骤

  1. 初始化阶段:创建渲染管线实例
  2. 相机排序:根据深度和设置排序相机
  3. 渲染设置:配置渲染参数和资源
  4. 通道执行:按顺序执行各个渲染通道
  5. 后处理:应用后处理效果
  6. 输出阶段:将结果输出到屏幕

渲染管线执行顺序

  • 深度预通道:渲染深度信息
  • 不透明通道:渲染不透明物体
  • 透明通道:渲染透明物体
  • 后期处理通道:应用后处理效果

代码示例

11.7 自定义RenderPipeline实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

// 自定义渲染管线Asset
public class CustomRenderPipelineAsset : RenderPipelineAsset
{
[SerializeField] private bool m_RenderSinglePassStereo = false;
[SerializeField] private bool m_RequireDepthTexture = false;
[SerializeField] private bool m_RequireOpaqueTexture = false;

protected override RenderPipeline CreatePipeline()
{
return new CustomRenderPipeline(
m_RenderSinglePassStereo,
m_RequireDepthTexture,
m_RequireOpaqueTexture
);
}

// 重写渲染管线创建方法
protected override void OnValidate()
{
base.OnValidate();
// 验证配置参数
if (m_RequireDepthTexture && m_RequireOpaqueTexture)
{
Debug.LogWarning("同时启用深度纹理和不透明纹理可能会增加内存使用");
}
}
}

// 自定义渲染管线实现
public class CustomRenderPipeline : RenderPipeline
{
private bool m_RenderSinglePassStereo;
private bool m_RequireDepthTexture;
private bool m_RequireOpaqueTexture;
private List<ShaderTagId> m_ShaderTags;

public CustomRenderPipeline(
bool renderSinglePassStereo,
bool requireDepthTexture,
bool requireOpaqueTexture)
{
m_RenderSinglePassStereo = renderSinglePassStereo;
m_RequireDepthTexture = requireDepthTexture;
m_RequireOpaqueTexture = requireOpaqueTexture;

// 初始化Shader标签列表
m_ShaderTags = new List<ShaderTagId>();
m_ShaderTags.Add(new ShaderTagId("SRPDefaultUnlit"));
m_ShaderTags.Add(new ShaderTagId("UniversalForward"));
m_ShaderTags.Add(new ShaderTagId("LightweightForward"));
}

public override void Render(ScriptableRenderContext renderContext, Camera[] cameras)
{
// 对相机进行排序
SortCameras(cameras);

// 为每个相机执行渲染
foreach (Camera camera in cameras)
{
RenderCamera(renderContext, camera);
}
}

private void SortCameras(Camera[] cameras)
{
// 按相机深度排序
System.Array.Sort(cameras, (a, b) => a.depth.CompareTo(b.depth));
}

private void RenderCamera(ScriptableRenderContext renderContext, Camera camera)
{
// 创建相机数据
var cameraData = CreateCameraData(camera);

// 设置渲染上下文
renderContext.SetupCameraProperties(camera);

// 执行渲染命令
CommandBuffer cmd = CommandBufferPool.Get("Custom Render Camera");

try
{
// 清除渲染目标
ClearRenderTarget(cmd, camera);

// 执行渲染通道
ExecuteRenderPasses(renderContext, cmd, cameraData);

// 提交命令
renderContext.ExecuteCommandBuffer(cmd);
}
finally
{
// 释放命令缓冲区
cmd.Clear();
CommandBufferPool.Release(cmd);
}

// 同步渲染上下文
renderContext.Submit();
}

private void ClearRenderTarget(CommandBuffer cmd, Camera camera)
{
var clearColor = camera.backgroundColor;
var clearFlags = camera.clearFlags;

cmd.ClearRenderTarget(
(ClearFlag)clearFlags,
clearColor,
camera.farClipPlane
);
}

private void ExecuteRenderPasses(
ScriptableRenderContext renderContext,
CommandBuffer cmd,
CameraData cameraData)
{
// 这里可以添加自定义的渲染通道
// 例如:深度预通道、不透明通道、透明通道等

// 执行不透明渲染
RenderOpaqueObjects(renderContext, cmd, cameraData);

// 执行透明渲染
RenderTransparentObjects(renderContext, cmd, cameraData);
}

private void RenderOpaqueObjects(
ScriptableRenderContext renderContext,
CommandBuffer cmd,
CameraData cameraData)
{
var drawingSettings = CreateDrawingSettings();
var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);

renderContext.DrawRenderers(
cameraData.cullResults,
ref drawingSettings,
ref filteringSettings
);
}

private void RenderTransparentObjects(
ScriptableRenderContext renderContext,
CommandBuffer cmd,
CameraData cameraData)
{
var drawingSettings = CreateDrawingSettings();
var filteringSettings = new FilteringSettings(RenderQueueRange.transparent);

renderContext.DrawRenderers(
cameraData.cullResults,
ref drawingSettings,
ref filteringSettings
);
}

private DrawingSettings CreateDrawingSettings()
{
var drawingSettings = new DrawingSettings();

// 设置Shader标签
for (int i = 0; i < m_ShaderTags.Count; ++i)
{
drawingSettings.SetShaderPassName(i, m_ShaderTags[i]);
}

// 设置排序方式
drawingSettings.sortingSettings = new SortingSettings(Camera.main)
{
criteria = SortingCriteria.CommonOpaque
};

return drawingSettings;
}

private CameraData CreateCameraData(Camera camera)
{
var cameraData = new CameraData();

// 设置相机参数
cameraData.camera = camera;
cameraData.isSceneViewCamera = camera.cameraType == CameraType.SceneView;
cameraData.isDefaultViewport = camera.rect == new Rect(0, 0, 1, 1);

// 执行视锥体裁剪
ScriptableCullingParameters cullingParams;
if (!camera.TryGetCullingParameters(out cullingParams))
return cameraData;

cameraData.cullResults = renderContext.Cull(ref cullingParams);

return cameraData;
}
}

// 相机数据结构
public struct CameraData
{
public Camera camera;
public CullingResults cullResults;
public bool isSceneViewCamera;
public bool isDefaultViewport;
public bool isHdrEnabled;
}

11.8 自定义ScriptableRenderer实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

// 自定义渲染器
public class CustomScriptableRenderer : ScriptableRenderer
{
private List<ScriptableRenderPass> m_ActiveRenderPassQueue = new List<ScriptableRenderPass>();
private List<RendererFeature> m_RendererFeatures = new List<RendererFeature>();

// 渲染器配置参数
private bool m_CreateDepthTexture;
private bool m_CreateOpaqueColorTexture;
private bool m_SupportsDynamicBatching;
private bool m_SupportsMixedLighting;

public CustomScriptableRenderer(
bool createDepthTexture,
bool createOpaqueColorTexture,
bool supportsDynamicBatching,
bool supportsMixedLighting)
{
m_CreateDepthTexture = createDepthTexture;
m_CreateOpaqueColorTexture = createOpaqueColorTexture;
m_SupportsDynamicBatching = supportsDynamicBatching;
m_SupportsMixedLighting = supportsMixedLighting;
}

public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
{
// 清空之前的渲染通道队列
m_ActiveRenderPassQueue.Clear();

// 配置渲染通道
ConfigureRenderPasses(ref renderingData);

// 添加渲染特性
AddRendererFeatures(ref renderingData);
}

private void ConfigureRenderPasses(ref RenderingData renderingData)
{
// 添加深度预通道(如果需要)
if (m_CreateDepthTexture)
{
var depthPrepass = new DepthOnlyPass(RenderPassEvent.BeforeRenderingOpaques);
m_ActiveRenderPassQueue.Add(depthPrepass);
}

// 添加不透明渲染通道
var opaquePass = new CustomOpaquePass(RenderPassEvent.BeforeRenderingOpaques);
m_ActiveRenderPassQueue.Add(opaquePass);

// 添加透明渲染通道
var transparentPass = new CustomTransparentPass(RenderPassEvent.BeforeRenderingTransparents);
m_ActiveRenderPassQueue.Add(transparentPass);

// 添加后处理通道(如果需要)
if (renderingData.postProcessingData.IsEnabled())
{
var postProcessPass = new CustomPostProcessPass(RenderPassEvent.AfterRenderingPostProcessing);
m_ActiveRenderPassQueue.Add(postProcessPass);
}
}

private void AddRendererFeatures(ref RenderingData renderingData)
{
// 这里可以添加自定义的渲染特性
// 遍历所有注册的渲染特性并添加到队列中
foreach (var feature in m_RendererFeatures)
{
if (feature?.isActive != false)
{
feature.AddRenderPasses(this, ref renderingData);
}
}
}

public override void EnqueuePass(ScriptableRenderPass pass)
{
if (pass != null)
{
m_ActiveRenderPassQueue.Add(pass);
}
}

public override void ConfigureCameraTarget(RenderTargetIdentifier cameraColorTarget, RenderTargetIdentifier cameraDepthTarget)
{
// 配置相机渲染目标
base.ConfigureCameraTarget(cameraColorTarget, cameraDepthTarget);
}

public override void SetupLights(ScriptableRenderContext context, ref RenderingData renderingData)
{
// 设置光照数据
base.SetupLights(context, ref renderingData);
}

public override void SetupCullingParameters(ref ScriptableCullingParameters cullingParameters, ref CameraData cameraData)
{
// 设置裁剪参数
base.SetupCullingParameters(ref cullingParameters, ref cameraData);
}

public override void Render(ScriptableRenderContext renderContext, ref RenderingData renderingData)
{
// 执行所有渲染通道
for (int i = 0; i < m_ActiveRenderPassQueue.Count; i++)
{
var pass = m_ActiveRenderPassQueue[i];
pass.Execute(renderContext, ref renderingData);
}

// 清理渲染通道
for (int i = 0; i < m_ActiveRenderPassQueue.Count; i++)
{
var pass = m_ActiveRenderPassQueue[i];
pass.FrameCleanup(ref renderingData);
}
}

// 添加渲染特性
public void AddRendererFeature(RendererFeature feature)
{
if (feature != null)
{
m_RendererFeatures.Add(feature);
}
}

// 移除渲染特性
public void RemoveRendererFeature(RendererFeature feature)
{
if (feature != null)
{
m_RendererFeatures.Remove(feature);
}
}

// 清空所有渲染特性
public void ClearRendererFeatures()
{
m_RendererFeatures.Clear();
}
}

// 自定义不透明渲染通道
public class CustomOpaquePass : ScriptableRenderPass
{
private FilteringSettings m_FilteringSettings;
private List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();

public CustomOpaquePass(RenderPassEvent renderPassEvent)
{
this.renderPassEvent = renderPassEvent;

// 添加常用的Shader标签
m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward"));
m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward"));

// 设置过滤条件
m_FilteringSettings = new FilteringSettings(RenderQueueRange.opaque);
}

public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
// 配置渲染通道
ConfigureTarget(cameraTextureDescriptor.colorFormat, cameraTextureDescriptor.depthBufferFormat);
ConfigureClear(ClearFlag.All, Color.black);
}

public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
var drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, SortingCriteria.CommonOpaque);
var filterSettings = m_FilteringSettings;

context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filterSettings);
}

public override void FrameCleanup(ref RenderingData renderingData)
{
// 清理帧资源
}
}

// 自定义透明渲染通道
public class CustomTransparentPass : ScriptableRenderPass
{
private FilteringSettings m_FilteringSettings;
private List<ShaderTagId> m_ShaderTagIdList = new List<ShaderTagId>();

public CustomTransparentPass(RenderPassEvent renderPassEvent)
{
this.renderPassEvent = renderPassEvent;

// 添加透明Shader标签
m_ShaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
m_ShaderTagIdList.Add(new ShaderTagId("UniversalForward"));
m_ShaderTagIdList.Add(new ShaderTagId("LightweightForward"));

// 设置透明渲染的过滤条件
m_FilteringSettings = new FilteringSettings(RenderQueueRange.transparent);
}

public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
ConfigureTarget(cameraTextureDescriptor.colorFormat, cameraTextureDescriptor.depthBufferFormat);
ConfigureClear(ClearFlag.None, Color.black);
}

public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
var drawingSettings = CreateDrawingSettings(m_ShaderTagIdList, ref renderingData, SortingCriteria.CommonTransparent);
var filterSettings = m_FilteringSettings;

context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filterSettings);
}

public override void FrameCleanup(ref RenderingData renderingData)
{
// 清理透明渲染通道资源
}
}

11.9 渲染管线资源管理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class RenderPipelineResourceManager : MonoBehaviour
{
[System.Serializable]
public class ResourcePoolConfig
{
public int initialPoolSize = 10;
public int maxPoolSize = 100;
public bool autoExpand = true;
}

[Header("Resource Pool Configuration")]
public ResourcePoolConfig commandBufferConfig = new ResourcePoolConfig();
public ResourcePoolConfig renderTextureConfig = new ResourcePoolConfig();

private Queue<CommandBuffer> m_CommandBufferPool = new Queue<CommandBuffer>();
private Queue<RenderTexture> m_RenderTexturePool = new Queue<RenderTexture>();

private List<RenderTexture> m_ActiveRenderTextures = new List<RenderTexture>();
private List<CommandBuffer> m_ActiveCommandBuffers = new List<CommandBuffer>();

void Start()
{
InitializeResourcePools();
}

private void InitializeResourcePools()
{
// 初始化命令缓冲区池
for (int i = 0; i < commandBufferConfig.initialPoolSize; i++)
{
var cmdBuffer = new CommandBuffer();
cmdBuffer.name = $"Pooled Command Buffer {i}";
m_CommandBufferPool.Enqueue(cmdBuffer);
}

// 初始化渲染纹理池
for (int i = 0; i < renderTextureConfig.initialPoolSize; i++)
{
var rt = new RenderTexture(256, 256, 24);
rt.name = $"Pooled Render Texture {i}";
m_RenderTexturePool.Enqueue(rt);
}
}

// 获取命令缓冲区
public CommandBuffer GetCommandBuffer(string name = "Default")
{
CommandBuffer cmdBuffer;

if (m_CommandBufferPool.Count > 0)
{
cmdBuffer = m_CommandBufferPool.Dequeue();
}
else if (commandBufferConfig.autoExpand &&
m_ActiveCommandBuffers.Count < commandBufferConfig.maxPoolSize)
{
cmdBuffer = new CommandBuffer();
}
else
{
Debug.LogWarning("Command buffer pool exhausted!");
return null;
}

cmdBuffer.name = name;
cmdBuffer.Clear();
m_ActiveCommandBuffers.Add(cmdBuffer);

return cmdBuffer;
}

// 释放命令缓冲区
public void ReleaseCommandBuffer(CommandBuffer cmdBuffer)
{
if (cmdBuffer == null) return;

if (m_CommandBufferPool.Count < commandBufferConfig.maxPoolSize)
{
cmdBuffer.Clear();
m_CommandBufferPool.Enqueue(cmdBuffer);
}
else
{
// 如果池已满,直接销毁
cmdBuffer.Dispose();
}

m_ActiveCommandBuffers.Remove(cmdBuffer);
}

// 获取渲染纹理
public RenderTexture GetRenderTexture(int width, int height, int depthBuffer = 24)
{
RenderTexture rt = null;

// 尝试从池中找到合适尺寸的纹理
var tempPool = new Queue<RenderTexture>();
while (m_RenderTexturePool.Count > 0)
{
var candidate = m_RenderTexturePool.Dequeue();
if (candidate.width == width && candidate.height == height && candidate.depth == depthBuffer)
{
rt = candidate;
break;
}
else
{
tempPool.Enqueue(candidate);
}
}

// 将不匹配的纹理放回池中
while (tempPool.Count > 0)
{
m_RenderTexturePool.Enqueue(tempPool.Dequeue());
}

if (rt == null)
{
if (renderTextureConfig.autoExpand &&
m_ActiveRenderTextures.Count < renderTextureConfig.maxPoolSize)
{
rt = new RenderTexture(width, height, depthBuffer);
}
else
{
Debug.LogWarning("Render texture pool exhausted!");
return null;
}
}

m_ActiveRenderTextures.Add(rt);
return rt;
}

// 释放渲染纹理
public void ReleaseRenderTexture(RenderTexture rt)
{
if (rt == null) return;

if (m_RenderTexturePool.Count < renderTextureConfig.maxPoolSize)
{
rt.Release();
m_RenderTexturePool.Enqueue(rt);
}
else
{
// 如果池已满,直接销毁
rt.Release();
DestroyImmediate(rt);
}

m_ActiveRenderTextures.Remove(rt);
}

// 获取资源使用统计
public string GetResourceStats()
{
return $"Command Buffers - Pool: {m_CommandBufferPool.Count}, Active: {m_ActiveCommandBuffers.Count}\n" +
$"Render Textures - Pool: {m_RenderTexturePool.Count}, Active: {m_ActiveRenderTextures.Count}";
}

// 清理所有资源
public void CleanupAllResources()
{
// 清理命令缓冲区
foreach (var cmdBuffer in m_ActiveCommandBuffers)
{
cmdBuffer.Dispose();
}
m_ActiveCommandBuffers.Clear();

while (m_CommandBufferPool.Count > 0)
{
var cmdBuffer = m_CommandBufferPool.Dequeue();
cmdBuffer.Dispose();
}

// 清理渲染纹理
foreach (var rt in m_ActiveRenderTextures)
{
rt.Release();
DestroyImmediate(rt);
}
m_ActiveRenderTextures.Clear();

while (m_RenderTexturePool.Count > 0)
{
var rt = m_RenderTexturePool.Dequeue();
rt.Release();
DestroyImmediate(rt);
}
}

void OnDestroy()
{
CleanupAllResources();
}
}

11.10 渲染管线调试工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class RenderPipelineDebugger : MonoBehaviour
{
[Header("Debug Settings")]
public bool enableRenderingDebug = true;
public bool showRenderPassInfo = true;
public bool logPerformanceData = false;

[Header("Performance Thresholds")]
public float warningFrameTime = 16.6f; // 60 FPS threshold
public int warningDrawCallCount = 100;
public int warningBatchCount = 100;

private List<float> frameTimes = new List<float>();
private int maxFrameTimes = 60;

[System.Serializable]
public class RenderPassInfo
{
public string name;
public RenderPassEvent passEvent;
public float executionTime;
public int drawCallCount;
public string description;
}

private List<RenderPassInfo> renderPassInfos = new List<RenderPassInfo>();
private Dictionary<string, float> performanceTimers = new Dictionary<string, float>();

void OnEnable()
{
if (enableRenderingDebug)
{
RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;
RenderPipelineManager.endCameraRendering += OnEndCameraRendering;
}
}

void OnDisable()
{
RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;
RenderPipelineManager.endCameraRendering -= OnEndCameraRendering;
}

private void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
{
if (!enableRenderingDebug) return;

// 记录渲染开始时间
string timerKey = $"Camera_{camera.GetInstanceID()}";
performanceTimers[timerKey] = Time.realtimeSinceStartup;

if (showRenderPassInfo)
{
Debug.Log($"[Render Debug] Starting render for camera: {camera.name}");
}
}

private void OnEndCameraRendering(ScriptableRenderContext context, Camera camera)
{
if (!enableRenderingDebug) return;

// 计算渲染时间
string timerKey = $"Camera_{camera.GetInstanceID()}";
if (performanceTimers.ContainsKey(timerKey))
{
float renderTime = (Time.realtimeSinceStartup - performanceTimers[timerKey]) * 1000f; // 转换为毫秒

if (logPerformanceData)
{
LogPerformanceData(camera, renderTime);
}

performanceTimers.Remove(timerKey);
}
}

private void LogPerformanceData(Camera camera, float renderTime)
{
int drawCalls = UnityEngine.Rendering.UnityRenderPipeline.activeRenderPassCount;
int batches = UnityEngine.Rendering.UnityRenderPipeline.batchCount;

string performanceInfo = $"[Performance] Camera: {camera.name} | " +
$"Render Time: {renderTime:F2}ms | " +
$"Draw Calls: {drawCalls} | " +
$"Batches: {batches}";

// 性能警告
if (renderTime > warningFrameTime)
{
Debug.LogWarning($"{performanceInfo} - Frame time exceeds threshold!");
}
else if (drawCalls > warningDrawCallCount)
{
Debug.LogWarning($"{performanceInfo} - High draw call count!");
}
else if (batches > warningBatchCount)
{
Debug.LogWarning($"{performanceInfo} - High batch count!");
}
else
{
Debug.Log(performanceInfo);
}
}

// 添加渲染通道信息
public void AddRenderPassInfo(string name, RenderPassEvent passEvent, float executionTime,
int drawCallCount, string description = "")
{
var info = new RenderPassInfo
{
name = name,
passEvent = passEvent,
executionTime = executionTime,
drawCallCount = drawCallCount,
description = description
};

renderPassInfos.Add(info);
}

// 获取渲染通道统计信息
public string GetRenderPassStats()
{
if (renderPassInfos.Count == 0) return "No render pass data available";

var stats = new System.Text.StringBuilder();
stats.AppendLine("Render Pass Statistics:");

foreach (var info in renderPassInfos)
{
stats.AppendLine($" {info.name} ({info.passEvent}): " +
$"Time={info.executionTime:F2}ms, " +
$"Draws={info.drawCallCount}");
}

return stats.ToString();
}

// 获取管线架构信息
public string GetPipelineArchitectureInfo()
{
var info = new System.Text.StringBuilder();

info.AppendLine("=== URP Pipeline Architecture ===");
info.AppendLine($"Render Pipeline Type: {RenderPipelineManager.currentPipeline.GetType().Name}");
info.AppendLine($"Active Render Pipeline: {(RenderPipelineManager.currentPipeline != null ? "Yes" : "No")}");
info.AppendLine($"SRP Batcher: {GraphicsSettings.useScriptableRenderPipelineBatching}");
info.AppendLine($"LOD CrossFade: {QualitySettings.lodBias}");

// 获取相机信息
var cameras = FindObjectsOfType<Camera>();
info.AppendLine($"Active Cameras: {cameras.Length}");

foreach (var cam in cameras)
{
var urpData = cam.GetUniversalAdditionalCameraData();
if (urpData != null)
{
info.AppendLine($" Camera '{cam.name}': " +
$"Renderer={urpData.rendererIndex}, " +
$"Render Type={urpData.renderType}, " +
$"Volume={urpData.volumeTrigger != null}");
}
}

return info.ToString();
}

// 性能分析
public void AnalyzePerformance()
{
float avgFrameTime = 0f;
if (frameTimes.Count > 0)
{
foreach (float time in frameTimes)
{
avgFrameTime += time;
}
avgFrameTime /= frameTimes.Count;
}

Debug.Log($"[Pipeline Analysis] Average Frame Time: {avgFrameTime:F2}ms, " +
$"Frame Times Recorded: {frameTimes.Count}");
}

void Update()
{
// 记录帧时间
float frameTime = Time.unscaledDeltaTime * 1000f;
frameTimes.Add(frameTime);

if (frameTimes.Count > maxFrameTimes)
{
frameTimes.RemoveAt(0);
}
}

// 清理调试数据
public void ClearDebugData()
{
renderPassInfos.Clear();
frameTimes.Clear();
performanceTimers.Clear();
}
}

实践练习

11.11 练习1:创建基础渲染管线

目标:实现一个最简单的自定义渲染管线

步骤

  1. 创建自定义RenderPipelineAsset
  2. 实现基础的RenderPipeline
  3. 配置基本的渲染流程
  4. 测试渲染管线是否正常工作
  5. 验证基本渲染功能

实现要点

  • 继承RenderPipelineAsset类
  • 重写CreatePipeline方法
  • 实现Render方法

11.12 练习2:实现自定义渲染器

目标:创建自定义ScriptableRenderer

步骤

  1. 创建CustomScriptableRenderer类
  2. 实现Setup和Render方法
  3. 添加基本的渲染通道
  4. 集成到渲染管线中
  5. 测试渲染器功能

关键技术

  • ScriptableRenderer基类继承
  • RenderPass管理
  • 资源配置

11.13 练习3:渲染通道调试

目标:实现渲染通道的调试功能

步骤

  1. 创建渲染通道信息收集器
  2. 记录每个通道的执行时间
  3. 显示通道执行顺序
  4. 分析性能瓶颈
  5. 优化通道执行

调试工具

  • 性能计时器
  • 通道执行日志
  • 可视化界面

11.14 练习4:资源管理优化

目标:实现渲染资源的池化管理

步骤

  1. 创建CommandBuffer池
  2. 实现RenderTexture池
  3. 添加资源生命周期管理
  4. 测试内存使用情况
  5. 优化池大小配置

优化策略

  • 对象池模式
  • 自动扩容机制
  • 资源复用

11.15 练习5:渲染管线性能分析

目标:实现渲染管线性能监控

步骤

  1. 集成Unity的渲染事件
  2. 记录渲染性能数据
  3. 分析性能瓶颈
  4. 生成性能报告
  5. 实现性能警告系统

监控指标

  • 帧时间
  • Draw Call数量
  • 批处理数量
  • 内存使用

总结

第11章深入探讨了URP渲染管线的架构设计,这是理解URP工作原理的基础。UniversalRenderPipeline作为Unity可编程渲染管线的核心实现,提供了灵活的渲染定制能力。

关键要点总结:

  1. 架构组件:RenderPipelineAsset、RenderPipeline、ScriptableRenderer的职责分工
  2. 渲染流程:从初始化到输出的完整渲染流程
  3. 资源管理:渲染资源的生命周期和池化管理
  4. 扩展性:通过ScriptableRenderPass和RendererFeature实现功能扩展
  5. 性能优化:渲染管线的性能监控和优化策略

URP的架构设计体现了可编程渲染管线的灵活性和扩展性,通过合理的架构分层,开发者可以根据项目需求定制渲染管线。理解这些底层架构对于优化渲染性能和实现特殊渲染效果至关重要。

下一章将深入分析Forward Renderer的具体实现,了解其内部工作原理和源码结构。