-
Notifications
You must be signed in to change notification settings - Fork 869
Expand file tree
/
Copy pathRenderGraphResources.cs
More file actions
288 lines (250 loc) · 10.5 KB
/
RenderGraphResources.cs
File metadata and controls
288 lines (250 loc) · 10.5 KB
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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
namespace UnityEngine.Rendering.RenderGraphModule
{
// RendererList is a different case so not represented here.
internal enum RenderGraphResourceType
{
Texture = 0,
Buffer,
AccelerationStructure,
Count
}
// For performance reasons, ResourceHandle is readonly.
// To update an existing instance with a new version, recreate it using its copy constructor
internal readonly struct ResourceHandle : IEquatable<ResourceHandle>
{
// Note on handles validity.
// PassData classes used during render graph passes are pooled and because of that, when users don't fill them completely,
// they can contain stale handles from a previous render graph execution that could still be considered valid if we only checked the index.
// 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.
// If not, it's considered invalid.
// We store this validity mask in the upper 16 bits of the index.
const uint kValidityMask = 0xFFFF0000;
const uint kIndexMask = 0xFFFF;
private readonly uint m_Value;
private readonly int m_Version;
private readonly RenderGraphResourceType m_Type;
static uint s_CurrentValidBit = 1 << 16;
static uint s_SharedResourceValidBit = 0x7FFF << 16;
public int index
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return (int)(m_Value & kIndexMask); }
}
public int iType
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return (int)type; }
}
public int version
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return m_Version; }
}
public RenderGraphResourceType type
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return m_Type; }
}
internal ResourceHandle(int value, RenderGraphResourceType type, bool shared)
{
Debug.Assert(value <= 0xFFFF);
m_Value = ((uint)value & kIndexMask) | (shared ? s_SharedResourceValidBit : s_CurrentValidBit);
m_Type = type;
m_Version = -1;
}
internal ResourceHandle(in ResourceHandle h, int version)
{
this.m_Value = h.m_Value;
this.m_Type = h.type;
this.m_Version = version;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsValid()
{
var validity = m_Value & kValidityMask;
return validity != 0 && (validity == s_CurrentValidBit || validity == s_SharedResourceValidBit);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsNull()
{
if (index == 0)
{
// Make sure everything is zero
Debug.Assert(m_Value == 0);
Debug.Assert(m_Version == 0);
return true;
}
return false;
}
static public void NewFrame(int executionIndex)
{
uint previousValidBit = s_CurrentValidBit;
// Scramble frame count to avoid collision when wrapping around.
s_CurrentValidBit = (uint)(((executionIndex >> 16) ^ (executionIndex & 0xffff) * 58546883) << 16);
// In case the current valid bit is 0, even though perfectly valid, 0 represents an invalid handle, hence we'll
// trigger an invalid state incorrectly. To account for this, we actually skip 0 as a viable s_CurrentValidBit and
// start from 1 again.
// In the same spirit, s_SharedResourceValidBit is reserved for shared textures so we should never use it otherwise
// resources could be considered valid at frame N+1 (because shared) even though they aren't.
if (s_CurrentValidBit == 0 || s_CurrentValidBit == s_SharedResourceValidBit)
{
// We need to make sure we don't pick the same value twice.
uint value = 1;
while (previousValidBit == (value << 16))
value++;
s_CurrentValidBit = (value << 16);
}
}
public bool IsVersioned
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
return m_Version >= 0;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(ResourceHandle hdl)
{
return hdl.m_Value == this.m_Value && hdl.m_Version == this.m_Version && hdl.type == this.type;
}
public static bool operator ==(ResourceHandle lhs, ResourceHandle rhs) => lhs.Equals(rhs);
public static bool operator !=(ResourceHandle lhs, ResourceHandle rhs) => !lhs.Equals(rhs);
public override bool Equals(object obj) => obj is ResourceHandle other && Equals(other);
public override int GetHashCode()
{
var hashCode = HashFNV1A32.Create();
hashCode.Append(m_Value);
hashCode.Append(m_Version);
hashCode.Append(m_Type);
return hashCode.value;
}
}
class IRenderGraphResource
{
public bool imported;
public bool shared;
public bool sharedExplicitRelease;
public bool requestFallBack;
public uint writeCount;
public uint readCount;
public int cachedHash;
public int transientPassIndex;
public int sharedResourceLastFrameUsed;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void Reset(IRenderGraphResourcePool _ = null)
{
imported = false;
shared = false;
sharedExplicitRelease = false;
cachedHash = -1;
transientPassIndex = -1;
sharedResourceLastFrameUsed = -1;
requestFallBack = false;
writeCount = 0;
readCount = 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual string GetName()
{
return "";
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual bool IsCreated()
{
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual uint IncrementWriteCount()
{
writeCount++;
return writeCount;
}
// readCount is currently not used in the HDRP Compiler.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual void IncrementReadCount()
{
readCount++;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual bool NeedsFallBack()
{
return requestFallBack && writeCount == 0;
}
public virtual void CreatePooledGraphicsResource(bool forceResourceCreation) { }
public virtual void CreateGraphicsResource() { }
public virtual void UpdateGraphicsResource() { }
public virtual void ReleasePooledGraphicsResource(int frameIndex) { }
public virtual void ReleaseGraphicsResource() { }
public virtual void LogCreation(RenderGraphLogger logger) { }
public virtual void LogRelease(RenderGraphLogger logger) { }
public virtual int GetSortIndex() { return 0; }
public virtual int GetDescHashCode() { return 0; }
}
[DebuggerDisplay("Resource ({GetType().Name}:{GetName()})")]
abstract class RenderGraphResource<DescType, ResType>
: IRenderGraphResource
where DescType : struct
where ResType : class
{
public DescType desc;
public bool validDesc; // Does the descriptor contain valid data (this is not always the case for imported resources)
public ResType graphicsResource;
protected RenderGraphResourcePool<ResType> m_Pool;
protected RenderGraphResource()
{
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void Reset(IRenderGraphResourcePool pool = null)
{
base.Reset();
m_Pool = pool as RenderGraphResourcePool<ResType>;
graphicsResource = null;
validDesc = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsCreated()
{
return graphicsResource != null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ReleaseGraphicsResource()
{
graphicsResource = null;
}
public override void CreatePooledGraphicsResource(bool forceResourceCreation)
{
Debug.Assert(m_Pool != null, "RenderGraphResource: CreatePooledGraphicsResource should only be called for regular pooled resources");
int hashCode = GetDescHashCode();
if (graphicsResource != null)
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.");
// If the pool doesn't have any available resource that we can use, we will create one
// In any case, we will update the graphicsResource name based on the RenderGraph resource name
if (forceResourceCreation || !m_Pool.TryGetResource(hashCode, out graphicsResource))
{
CreateGraphicsResource();
}
else
{
UpdateGraphicsResource();
}
cachedHash = hashCode;
m_Pool.RegisterFrameAllocation(cachedHash, graphicsResource);
}
public override void ReleasePooledGraphicsResource(int frameIndex)
{
if (graphicsResource == null)
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.");
// Shared resources don't use the pool
if (m_Pool != null)
{
m_Pool.ReleaseResource(cachedHash, graphicsResource, frameIndex);
m_Pool.UnregisterFrameAllocation(cachedHash, graphicsResource);
}
Reset();
}
}
}