A game about forced loneliness, made by TACStudios
1using System;
2using System.Diagnostics;
3using System.Runtime.CompilerServices;
4
5namespace UnityEngine.Rendering.RenderGraphModule
6{
7 // RendererList is a different case so not represented here.
8 internal enum RenderGraphResourceType
9 {
10 Texture = 0,
11 Buffer,
12 AccelerationStructure,
13 Count
14 }
15
16 internal struct ResourceHandle : IEquatable<ResourceHandle>
17 {
18 // Note on handles validity.
19 // PassData classes used during render graph passes are pooled and because of that, when users don't fill them completely,
20 // they can contain stale handles from a previous render graph execution that could still be considered valid if we only checked the index.
21 // In order to avoid using those, we incorporate the execution index in a 16 bits hash to make sure the handle is coming from the current execution.
22 // If not, it's considered invalid.
23 // We store this validity mask in the upper 16 bits of the index.
24 const uint kValidityMask = 0xFFFF0000;
25 const uint kIndexMask = 0xFFFF;
26
27 uint m_Value;
28 int m_Version; // A freshly created resource always starts at version 0 the first write should bring it to v1
29
30 static uint s_CurrentValidBit = 1 << 16;
31 static uint s_SharedResourceValidBit = 0x7FFF << 16;
32
33 public int index
34 {
35 [MethodImpl(MethodImplOptions.AggressiveInlining)]
36 get { return (int)(m_Value & kIndexMask); }
37 }
38 public int iType
39 {
40 [MethodImpl(MethodImplOptions.AggressiveInlining)]
41 get { return (int)type; }
42 }
43 public int version
44 {
45 [MethodImpl(MethodImplOptions.AggressiveInlining)]
46 get { return m_Version; }
47 [MethodImpl(MethodImplOptions.AggressiveInlining)]
48 set { m_Version = value; }
49 }
50 public RenderGraphResourceType type { get; private set; }
51
52 internal ResourceHandle(int value, RenderGraphResourceType type, bool shared)
53 {
54 Debug.Assert(value <= 0xFFFF);
55 m_Value = ((uint)value & kIndexMask) | (shared ? s_SharedResourceValidBit : s_CurrentValidBit);
56 this.type = type;
57 this.m_Version = -1;
58 }
59
60 internal ResourceHandle(in ResourceHandle h, int version)
61 {
62 this.m_Value = h.m_Value;
63 this.type = h.type;
64 this.m_Version = version;
65 }
66
67 [MethodImpl(MethodImplOptions.AggressiveInlining)]
68 public bool IsValid()
69 {
70 var validity = m_Value & kValidityMask;
71 return validity != 0 && (validity == s_CurrentValidBit || validity == s_SharedResourceValidBit);
72 }
73
74 [MethodImpl(MethodImplOptions.AggressiveInlining)]
75 public bool IsNull()
76 {
77 if (index == 0)
78 {
79 // Make sure everything is zero
80 Debug.Assert(m_Value == 0);
81 Debug.Assert(m_Version == 0);
82 return true;
83 }
84 return false;
85 }
86
87 static public void NewFrame(int executionIndex)
88 {
89 uint previousValidBit = s_CurrentValidBit;
90 // Scramble frame count to avoid collision when wrapping around.
91 s_CurrentValidBit = (uint)(((executionIndex >> 16) ^ (executionIndex & 0xffff) * 58546883) << 16);
92 // In case the current valid bit is 0, even though perfectly valid, 0 represents an invalid handle, hence we'll
93 // trigger an invalid state incorrectly. To account for this, we actually skip 0 as a viable s_CurrentValidBit and
94 // start from 1 again.
95 // In the same spirit, s_SharedResourceValidBit is reserved for shared textures so we should never use it otherwise
96 // resources could be considered valid at frame N+1 (because shared) even though they aren't.
97 if (s_CurrentValidBit == 0 || s_CurrentValidBit == s_SharedResourceValidBit)
98 {
99 // We need to make sure we don't pick the same value twice.
100 uint value = 1;
101 while (previousValidBit == (value << 16))
102 value++;
103 s_CurrentValidBit = (value << 16);
104 }
105 }
106
107 public bool IsVersioned
108 {
109 [MethodImpl(MethodImplOptions.AggressiveInlining)]
110 get
111 {
112 return m_Version >= 0;
113 }
114 }
115
116 [MethodImpl(MethodImplOptions.AggressiveInlining)]
117 public bool Equals(ResourceHandle hdl)
118 {
119 return hdl.m_Value == this.m_Value && hdl.m_Version == this.m_Version && hdl.type == this.type;
120 }
121 }
122
123 class IRenderGraphResource
124 {
125 public bool imported;
126 public bool shared;
127 public bool sharedExplicitRelease;
128 public bool requestFallBack;
129 public bool forceRelease;
130 public uint writeCount;
131 public int cachedHash;
132 public int transientPassIndex;
133 public int sharedResourceLastFrameUsed;
134 public int version;
135
136 [MethodImpl(MethodImplOptions.AggressiveInlining)]
137 public virtual void Reset(IRenderGraphResourcePool _ = null)
138 {
139 imported = false;
140 shared = false;
141 sharedExplicitRelease = false;
142 cachedHash = -1;
143 transientPassIndex = -1;
144 sharedResourceLastFrameUsed = -1;
145 requestFallBack = false;
146 forceRelease = false;
147 writeCount = 0;
148 version = 0;
149 }
150
151 [MethodImpl(MethodImplOptions.AggressiveInlining)]
152 public virtual string GetName()
153 {
154 return "";
155 }
156
157 [MethodImpl(MethodImplOptions.AggressiveInlining)]
158 public virtual bool IsCreated()
159 {
160 return false;
161 }
162
163 [MethodImpl(MethodImplOptions.AggressiveInlining)]
164 public virtual void IncrementWriteCount()
165 {
166 writeCount++;
167 }
168
169 [MethodImpl(MethodImplOptions.AggressiveInlining)]
170 public virtual int NewVersion()
171 {
172 version++;
173 return version;
174 }
175
176 [MethodImpl(MethodImplOptions.AggressiveInlining)]
177 public virtual bool NeedsFallBack()
178 {
179 return requestFallBack && writeCount == 0;
180 }
181
182 public virtual void CreatePooledGraphicsResource() { }
183 public virtual void CreateGraphicsResource() { }
184 public virtual void UpdateGraphicsResource() { }
185 public virtual void ReleasePooledGraphicsResource(int frameIndex) { }
186 public virtual void ReleaseGraphicsResource() { }
187 public virtual void LogCreation(RenderGraphLogger logger) { }
188 public virtual void LogRelease(RenderGraphLogger logger) { }
189 public virtual int GetSortIndex() { return 0; }
190 public virtual int GetDescHashCode() { return 0; }
191 }
192
193 [DebuggerDisplay("Resource ({GetType().Name}:{GetName()})")]
194 abstract class RenderGraphResource<DescType, ResType>
195 : IRenderGraphResource
196 where DescType : struct
197 where ResType : class
198 {
199 public DescType desc;
200 public bool validDesc; // Does the descriptor contain valid data (this is not always the case for imported resources)
201 public ResType graphicsResource;
202
203 protected RenderGraphResourcePool<ResType> m_Pool;
204
205 protected RenderGraphResource()
206 {
207 }
208
209 [MethodImpl(MethodImplOptions.AggressiveInlining)]
210 public override void Reset(IRenderGraphResourcePool pool = null)
211 {
212 base.Reset();
213 m_Pool = pool as RenderGraphResourcePool<ResType>;
214 graphicsResource = null;
215 validDesc = false;
216 }
217
218 [MethodImpl(MethodImplOptions.AggressiveInlining)]
219 public override bool IsCreated()
220 {
221 return graphicsResource != null;
222 }
223
224 [MethodImpl(MethodImplOptions.AggressiveInlining)]
225 public override void ReleaseGraphicsResource()
226 {
227 graphicsResource = null;
228 }
229
230 public override void CreatePooledGraphicsResource()
231 {
232 Debug.Assert(m_Pool != null, "RenderGraphResource: CreatePooledGraphicsResource should only be called for regular pooled resources");
233
234 int hashCode = GetDescHashCode();
235
236 if (graphicsResource != null)
237 throw new InvalidOperationException($"RenderGraphResource: Trying to create an already created resource ({GetName()}). Resource was probably declared for writing more than once in the same pass.");
238
239 // If the pool doesn't have any available resource that we can use, we will create one
240 // In any case, we will update the graphicsResource name based on the RenderGraph resource name
241 if (!m_Pool.TryGetResource(hashCode, out graphicsResource))
242 {
243 CreateGraphicsResource();
244 }
245 else
246 {
247 UpdateGraphicsResource();
248 }
249
250 cachedHash = hashCode;
251 m_Pool.RegisterFrameAllocation(cachedHash, graphicsResource);
252 }
253
254 public override void ReleasePooledGraphicsResource(int frameIndex)
255 {
256 if (graphicsResource == null)
257 throw new InvalidOperationException($"RenderGraphResource: Tried to release a resource ({GetName()}) that was never created. Check that there is at least one pass writing to it first.");
258
259 // Shared resources don't use the pool
260 if (m_Pool != null)
261 {
262 m_Pool.ReleaseResource(cachedHash, graphicsResource, frameIndex);
263 m_Pool.UnregisterFrameAllocation(cachedHash, graphicsResource);
264 }
265
266 Reset();
267 }
268 }
269}