A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using UnityEngine.Assertions;
4using UnityEngine.Rendering;
5
6namespace UnityEngine.Rendering
7{
8 /// <summary>
9 /// Implement a multiple buffering for RenderTextures.
10 /// </summary>
11 /// <example>
12 /// <code>
13 /// enum BufferType
14 /// {
15 /// Color,
16 /// Depth
17 /// }
18 ///
19 /// void Render()
20 /// {
21 /// var camera = GetCamera();
22 /// var buffers = GetFrameHistoryBuffersFor(camera);
23 ///
24 /// // Set reference size in case the rendering size changed this frame
25 /// buffers.SetReferenceSize(
26 /// GetCameraWidth(camera), GetCameraHeight(camera),
27 /// GetCameraUseMSAA(camera), GetCameraMSAASamples(camera)
28 /// );
29 /// buffers.Swap();
30 ///
31 /// var currentColor = buffer.GetFrameRT((int)BufferType.Color, 0);
32 /// if (currentColor == null) // Buffer was not allocated
33 /// {
34 /// buffer.AllocBuffer(
35 /// (int)BufferType.Color, // Color buffer id
36 /// ColorBufferAllocator, // Custom functor to implement allocation
37 /// 2 // Use 2 RT for this buffer for double buffering
38 /// );
39 /// currentColor = buffer.GetFrameRT((int)BufferType.Color, 0);
40 /// }
41 ///
42 /// var previousColor = buffers.GetFrameRT((int)BufferType.Color, 1);
43 ///
44 /// // Use previousColor and write into currentColor
45 /// }
46 /// </code>
47 /// </example>
48 public class BufferedRTHandleSystem : IDisposable
49 {
50 Dictionary<int, RTHandle[]> m_RTHandles = new Dictionary<int, RTHandle[]>();
51
52 RTHandleSystem m_RTHandleSystem = new RTHandleSystem();
53 bool m_DisposedValue = false;
54
55 /// <summary>
56 /// Maximum allocated width of the Buffered RTHandle System
57 /// </summary>
58 public int maxWidth { get { return m_RTHandleSystem.GetMaxWidth(); } }
59 /// <summary>
60 /// Maximum allocated height of the Buffered RTHandle System
61 /// </summary>
62 public int maxHeight { get { return m_RTHandleSystem.GetMaxHeight(); } }
63 /// <summary>
64 /// Current properties of the Buffered RTHandle System
65 /// </summary>
66 public RTHandleProperties rtHandleProperties { get { return m_RTHandleSystem.rtHandleProperties; } }
67
68 /// <summary>
69 /// Return the frame RT or null.
70 /// </summary>
71 /// <param name="bufferId">Defines the buffer to use.</param>
72 /// <param name="frameIndex">Defines which frame to access within the buffer.</param>
73 /// <returns>The frame RT or null when the <paramref name="bufferId"/> was not previously allocated (<see cref="BufferedRTHandleSystem.AllocBuffer(int, Func{RTHandleSystem, int, RTHandle}, int)" />).</returns>
74 public RTHandle GetFrameRT(int bufferId, int frameIndex)
75 {
76 if (!m_RTHandles.ContainsKey(bufferId))
77 return null;
78
79 Assert.IsTrue(frameIndex >= 0 && frameIndex < m_RTHandles[bufferId].Length);
80
81 return m_RTHandles[bufferId][frameIndex];
82 }
83
84 /// <summary>
85 /// Clears all the previously created history buffers
86 /// </summary>
87 /// <param name="cmd">Defines the command buffer used for clearing.</param>
88
89 public void ClearBuffers(CommandBuffer cmd)
90 {
91 foreach (var rtHandle in m_RTHandles)
92 {
93 for (int i = 0; i < rtHandle.Value.Length; ++i)
94 {
95 CoreUtils.SetRenderTarget(cmd, rtHandle.Value[i], clearFlag: ClearFlag.Color, clearColor: Color.black);
96 }
97 }
98 }
99
100 /// <summary>
101 /// Allocate RT handles for a buffer.
102 /// </summary>
103 /// <param name="bufferId">The buffer to allocate.</param>
104 /// <param name="allocator">The functor to use for allocation.</param>
105 /// <param name="bufferCount">The number of RT handles for this buffer.</param>
106 public void AllocBuffer(
107 int bufferId,
108 Func<RTHandleSystem, int, RTHandle> allocator,
109 int bufferCount
110 )
111 {
112 // This function should only be used when there is a non-zero number of buffers to allocate.
113 // If the caller provides a value of zero, they're likely doing something unintentional in the calling code.
114 Debug.Assert(bufferCount > 0);
115
116 var buffer = new RTHandle[bufferCount];
117 m_RTHandles.Add(bufferId, buffer);
118
119 // First is autoresized
120 buffer[0] = allocator(m_RTHandleSystem, 0);
121
122 // Other are resized on demand
123 for (int i = 1, c = buffer.Length; i < c; ++i)
124 {
125 buffer[i] = allocator(m_RTHandleSystem, i);
126 m_RTHandleSystem.SwitchResizeMode(buffer[i], RTHandleSystem.ResizeMode.OnDemand);
127 }
128 }
129
130 /// <summary>
131 /// Allocate RT handles for a buffer using a RenderTextureDescriptor.
132 /// </summary>
133 /// <param name="bufferId">The buffer to allocate.</param>
134 /// <param name="bufferCount">The number of RT handles for this buffer.</param>
135 /// <param name="descriptor">RenderTexture descriptor of the RTHandles.</param>
136 /// <param name="filterMode">Filtering mode of the RTHandles.</param>
137 /// <param name="wrapMode">Addressing mode of the RTHandles.</param>
138 /// <param name="isShadowMap">Set to true if the depth buffer should be used as a shadow map.</param>
139 /// <param name="anisoLevel">Anisotropic filtering level.</param>
140 /// <param name="mipMapBias">Bias applied to mipmaps during filtering.</param>
141 /// <param name="name">Name of the RTHandle.</param>
142 // NOTE: API is similar to RTHandles.Alloc.
143 public void AllocBuffer(int bufferId, int bufferCount,
144 ref RenderTextureDescriptor descriptor,
145 FilterMode filterMode = FilterMode.Point,
146 TextureWrapMode wrapMode = TextureWrapMode.Repeat,
147 bool isShadowMap = false,
148 int anisoLevel = 1,
149 float mipMapBias = 0,
150 string name = "")
151 {
152 // This function should only be used when there is a non-zero number of buffers to allocate.
153 // If the caller provides a value of zero, they're likely doing something unintentional in the calling code.
154 Debug.Assert(bufferCount > 0);
155
156 var buffer = new RTHandle[bufferCount];
157 m_RTHandles.Add(bufferId, buffer);
158
159 var format = RTHandles.GetFormat(descriptor.graphicsFormat, descriptor.depthStencilFormat);
160
161 RTHandle Alloc(ref RenderTextureDescriptor d, FilterMode fMode, TextureWrapMode wMode, bool isShadow, int aniso, float mipBias, string n)
162 {
163 return m_RTHandleSystem.Alloc(
164 d.width,
165 d.height,
166 format,
167 d.volumeDepth,
168 fMode,
169 wMode,
170 d.dimension,
171 d.enableRandomWrite,
172 d.useMipMap,
173 d.autoGenerateMips,
174 isShadow,
175 aniso,
176 mipBias,
177 (MSAASamples)d.msaaSamples,
178 d.bindMS,
179 d.useDynamicScale,
180 d.useDynamicScaleExplicit,
181 d.memoryless,
182 d.vrUsage,
183 n);
184 }
185
186 // First is autoresized
187 buffer[0] = Alloc(ref descriptor, filterMode, wrapMode, isShadowMap, anisoLevel, mipMapBias, name);
188
189 // Other are resized on demand
190 for (int i = 1, c = buffer.Length; i < c; ++i)
191 {
192 buffer[i] = Alloc(ref descriptor, filterMode, wrapMode, isShadowMap, anisoLevel, mipMapBias, name);
193 m_RTHandleSystem.SwitchResizeMode(buffer[i], RTHandleSystem.ResizeMode.OnDemand);
194 }
195 }
196
197 /// <summary>
198 /// Release a buffer
199 /// </summary>
200 /// <param name="bufferId">Id of the buffer that needs to be released.</param>
201 public void ReleaseBuffer(int bufferId)
202 {
203 if (m_RTHandles.TryGetValue(bufferId, out var buffers))
204 {
205 foreach (var rt in buffers)
206 m_RTHandleSystem.Release(rt);
207 }
208
209 m_RTHandles.Remove(bufferId);
210 }
211
212 /// <summary>
213 /// Swap buffers Set the reference size for this RT Handle System (<see cref="RTHandleSystem.SetReferenceSize(int, int, bool)"/>)
214 /// </summary>
215 /// <param name="width">The width of the RTs of this buffer.</param>
216 /// <param name="height">The height of the RTs of this buffer.</param>
217 public void SwapAndSetReferenceSize(int width, int height)
218 {
219 Swap();
220 m_RTHandleSystem.SetReferenceSize(width, height);
221 }
222
223 /// <summary>
224 /// Reset the reference size of the system and reallocate all textures.
225 /// </summary>
226 /// <param name="width">New width.</param>
227 /// <param name="height">New height.</param>
228 public void ResetReferenceSize(int width, int height)
229 {
230 m_RTHandleSystem.ResetReferenceSize(width, height);
231 }
232
233 /// <summary>
234 /// Queries the number of RT handle buffers allocated for a buffer ID.
235 /// </summary>
236 /// <param name="bufferId">The buffer ID to query.</param>
237 /// <returns>The num of frames allocated</returns>
238 public int GetNumFramesAllocated(int bufferId)
239 {
240 if (!m_RTHandles.ContainsKey(bufferId))
241 return 0;
242
243 return m_RTHandles[bufferId].Length;
244 }
245
246 /// <summary>
247 /// Returns the ratio against the current target's max resolution
248 /// </summary>
249 /// <param name="width">width to utilize</param>
250 /// <param name="height">height to utilize</param>
251 /// <returns> retruns the width,height / maxTargetSize.xy ratio. </returns>
252 public Vector2 CalculateRatioAgainstMaxSize(int width, int height)
253 {
254 return m_RTHandleSystem.CalculateRatioAgainstMaxSize(new Vector2Int(width, height));
255 }
256
257 void Swap()
258 {
259 foreach (var item in m_RTHandles)
260 {
261 // Do not index out of bounds...
262 if (item.Value.Length > 1)
263 {
264 var nextFirst = item.Value[item.Value.Length - 1];
265 for (int i = 0, c = item.Value.Length - 1; i < c; ++i)
266 item.Value[i + 1] = item.Value[i];
267 item.Value[0] = nextFirst;
268
269 // First is autoresize, other are on demand
270 m_RTHandleSystem.SwitchResizeMode(item.Value[0], RTHandleSystem.ResizeMode.Auto);
271 m_RTHandleSystem.SwitchResizeMode(item.Value[1], RTHandleSystem.ResizeMode.OnDemand);
272 }
273 else
274 {
275 m_RTHandleSystem.SwitchResizeMode(item.Value[0], RTHandleSystem.ResizeMode.Auto);
276 }
277 }
278 }
279
280 void Dispose(bool disposing)
281 {
282 if (!m_DisposedValue)
283 {
284 if (disposing)
285 {
286 ReleaseAll();
287 m_RTHandleSystem.Dispose();
288 m_RTHandleSystem = null;
289 }
290
291 m_DisposedValue = true;
292 }
293 }
294
295 /// <summary>
296 /// Dispose implementation
297 /// </summary>
298 public void Dispose()
299 {
300 Dispose(true);
301 }
302
303 /// <summary>
304 /// Deallocate and clear all buffers.
305 /// </summary>
306 public void ReleaseAll()
307 {
308 foreach (var item in m_RTHandles)
309 {
310 for (int i = 0, c = item.Value.Length; i < c; ++i)
311 {
312 m_RTHandleSystem.Release(item.Value[i]);
313 }
314 }
315 m_RTHandles.Clear();
316 }
317 }
318}