Skip to content
This repository was archived by the owner on Nov 30, 2020. It is now read-only.

Commit fccc1ad

Browse files
committed
Fixed broken layer masking & priority management; CPU optimizations in the manager
1 parent 5131521 commit fccc1ad

1 file changed

Lines changed: 138 additions & 126 deletions

File tree

PostProcessing/Runtime/PostProcessManager.cs

Lines changed: 138 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,19 @@ public static PostProcessManager instance
2323
}
2424

2525
const int k_MaxLayerCount = 32; // Max amount of layers available in Unity
26-
readonly List<PostProcessVolume>[] m_Volumes;
27-
readonly bool[] m_SortNeeded;
26+
readonly Dictionary<LayerMask, List<PostProcessVolume>> m_SortedVolumes;
27+
readonly List<PostProcessVolume> m_Volumes;
28+
readonly Dictionary<LayerMask, bool> m_SortNeeded;
2829
readonly List<PostProcessEffectSettings> m_BaseSettings;
2930
readonly List<Collider> m_TempColliders;
3031

3132
public readonly Dictionary<Type, PostProcessAttribute> settingsTypes;
3233

3334
PostProcessManager()
3435
{
35-
m_Volumes = new List<PostProcessVolume>[k_MaxLayerCount];
36-
m_SortNeeded = new bool[k_MaxLayerCount];
36+
m_SortedVolumes = new Dictionary<LayerMask, List<PostProcessVolume>>();
37+
m_Volumes = new List<PostProcessVolume>();
38+
m_SortNeeded = new Dictionary<LayerMask, bool>();
3739
m_BaseSettings = new List<PostProcessEffectSettings>();
3840
m_TempColliders = new List<Collider>(5);
3941

@@ -97,63 +99,53 @@ public void GetActiveVolumes(PostProcessLayer layer, List<PostProcessVolume> res
9799
bool onlyGlobal = volumeTrigger == null;
98100
var triggerPos = onlyGlobal ? Vector3.zero : volumeTrigger.position;
99101

100-
for (int i = 0; i < k_MaxLayerCount; i++)
102+
// Sort the cached volume list(s) for the given layer mask if needed and return it
103+
var volumes = GrabVolumes(mask);
104+
105+
// Traverse all volumes
106+
foreach (var volume in volumes)
101107
{
102-
// Skip layers not in the mask
103-
if ((mask & (1 << i)) == 0)
108+
// Skip disabled volumes and volumes without any data or weight
109+
if (!volume.enabled || volume.profileRef == null || volume.weight <= 0f)
104110
continue;
105111

106-
// Skip empty layers
107-
var volumes = m_Volumes[i];
108-
109-
if (volumes == null)
112+
// Global volume always have influence
113+
if (volume.isGlobal)
114+
{
115+
results.Add(volume);
110116
continue;
117+
}
111118

112-
// Traverse all volumes
113-
foreach (var volume in volumes)
114-
{
115-
// Skip disabled volumes and volumes without any data or weight
116-
if (!volume.enabled || volume.profileRef == null || volume.weight <= 0f)
117-
continue;
119+
if (onlyGlobal)
120+
continue;
118121

119-
// Global volume always have influence
120-
if (volume.isGlobal)
121-
{
122-
results.Add(volume);
123-
continue;
124-
}
122+
// If volume isn't global and has no collider, skip it as it's useless
123+
var colliders = m_TempColliders;
124+
volume.GetComponents(colliders);
125+
if (colliders.Count == 0)
126+
continue;
125127

126-
if (onlyGlobal)
127-
continue;
128+
// Find closest distance to volume, 0 means it's inside it
129+
float closestDistanceSqr = float.PositiveInfinity;
128130

129-
// If volume isn't global and has no collider, skip it as it's useless
130-
var colliders = m_TempColliders;
131-
volume.GetComponents(colliders);
132-
if (colliders.Count == 0)
131+
foreach (var collider in colliders)
132+
{
133+
if (!collider.enabled)
133134
continue;
134135

135-
// Find closest distance to volume, 0 means it's inside it
136-
float closestDistanceSqr = float.PositiveInfinity;
137-
138-
foreach (var collider in colliders)
139-
{
140-
if (!collider.enabled)
141-
continue;
142-
143-
var closestPoint = collider.ClosestPoint(triggerPos); // 5.6-only API
144-
var d = ((closestPoint - triggerPos) / 2f).sqrMagnitude;
136+
var closestPoint = collider.ClosestPoint(triggerPos); // 5.6-only API
137+
var d = ((closestPoint - triggerPos) / 2f).sqrMagnitude;
145138

146-
if (d < closestDistanceSqr)
147-
closestDistanceSqr = d;
148-
}
139+
if (d < closestDistanceSqr)
140+
closestDistanceSqr = d;
141+
}
149142

150-
colliders.Clear();
151-
float blendDistSqr = volume.blendDistance * volume.blendDistance;
143+
colliders.Clear();
144+
float blendDistSqr = volume.blendDistance * volume.blendDistance;
152145

153-
// Check for influence
154-
if (closestDistanceSqr <= blendDistSqr)
155-
results.Add(volume);
156-
}
146+
// Check for influence
147+
if (closestDistanceSqr <= blendDistSqr)
148+
results.Add(volume);
157149
}
158150
}
159151

@@ -170,18 +162,9 @@ public PostProcessVolume GetHighestPriorityVolume(LayerMask mask)
170162
float highestPriority = float.NegativeInfinity;
171163
PostProcessVolume output = null;
172164

173-
for (int i = 0; i < k_MaxLayerCount; i++)
165+
List<PostProcessVolume> volumes;
166+
if (m_SortedVolumes.TryGetValue(mask, out volumes))
174167
{
175-
// Skip layers not in the mask
176-
if ((mask & (1 << i)) == 0)
177-
continue;
178-
179-
// Skip empty layers
180-
var volumes = m_Volumes[i];
181-
182-
if (volumes == null)
183-
continue;
184-
185168
foreach (var volume in volumes)
186169
{
187170
if (volume.priority > highestPriority)
@@ -221,7 +204,14 @@ public PostProcessVolume QuickVolume(int layer, float priority, params PostProce
221204
internal void SetLayerDirty(int layer)
222205
{
223206
Assert.IsTrue(layer >= 0 && layer <= k_MaxLayerCount, "Invalid layer bit");
224-
m_SortNeeded[layer] = true;
207+
208+
foreach (var kvp in m_SortedVolumes)
209+
{
210+
var mask = kvp.Key;
211+
212+
if ((mask & (1 << layer)) != 0)
213+
m_SortNeeded[mask] = true;
214+
}
225215
}
226216

227217
internal void UpdateVolumeLayer(PostProcessVolume volume, int prevLayer, int newLayer)
@@ -233,16 +223,17 @@ internal void UpdateVolumeLayer(PostProcessVolume volume, int prevLayer, int new
233223

234224
void Register(PostProcessVolume volume, int layer)
235225
{
236-
var volumes = m_Volumes[layer];
226+
m_Volumes.Add(volume);
237227

238-
if (volumes == null)
228+
// Look for existing cached layer masks and add it there if needed
229+
foreach (var kvp in m_SortedVolumes)
239230
{
240-
volumes = new List<PostProcessVolume>();
241-
m_Volumes[layer] = volumes;
231+
var mask = kvp.Key;
232+
233+
if ((mask & (1 << layer)) != 0)
234+
kvp.Value.Add(volume);
242235
}
243236

244-
Assert.IsFalse(volumes.Contains(volume), "Volume has already been registered");
245-
volumes.Add(volume);
246237
SetLayerDirty(layer);
247238
}
248239

@@ -254,13 +245,18 @@ internal void Register(PostProcessVolume volume)
254245

255246
void Unregister(PostProcessVolume volume, int layer)
256247
{
257-
var volumes = m_Volumes[layer];
248+
m_Volumes.Remove(volume);
258249

259-
if (volumes == null)
260-
return;
250+
foreach (var kvp in m_SortedVolumes)
251+
{
252+
var mask = kvp.Key;
261253

262-
Assert.IsTrue(volumes.Contains(volume), "Trying to unregister a non-registered volume");
263-
volumes.Remove(volume);
254+
// Skip layer masks this volume doesn't belong to
255+
if ((mask & (1 << layer)) == 0)
256+
continue;
257+
258+
kvp.Value.Remove(volume);
259+
}
264260
}
265261

266262
internal void Unregister(PostProcessVolume volume)
@@ -280,85 +276,101 @@ internal void UpdateSettings(PostProcessLayer postProcessLayer)
280276
bool onlyGlobal = volumeTrigger == null;
281277
var triggerPos = onlyGlobal ? Vector3.zero : volumeTrigger.position;
282278

283-
for (int i = 0; i < k_MaxLayerCount; i++)
279+
// Sort the cached volume list(s) for the given layer mask if needed and return it
280+
var volumes = GrabVolumes(mask);
281+
282+
// Traverse all volumes
283+
foreach (var volume in volumes)
284284
{
285-
// Skip layers not in the mask
286-
if ((mask & (1 << i)) == 0)
285+
// Skip disabled volumes and volumes without any data or weight
286+
if (!volume.enabled || volume.profileRef == null || volume.weight <= 0f)
287287
continue;
288288

289-
// Skip empty layers
290-
var volumes = m_Volumes[i];
291-
292-
if (volumes == null)
293-
continue;
289+
var settings = volume.profileRef.settings;
294290

295-
// Sort the volume list if needed
296-
if (m_SortNeeded[i])
291+
// Global volume always have influence
292+
if (volume.isGlobal)
297293
{
298-
SortByPriority(volumes);
299-
m_SortNeeded[i] = false;
294+
postProcessLayer.OverrideSettings(settings, Mathf.Clamp01(volume.weight));
295+
continue;
300296
}
301297

302-
// Traverse all volumes
303-
foreach (var volume in volumes)
298+
if (onlyGlobal)
299+
continue;
300+
301+
// If volume isn't global and has no collider, skip it as it's useless
302+
var colliders = m_TempColliders;
303+
volume.GetComponents(colliders);
304+
if (colliders.Count == 0)
305+
continue;
306+
307+
// Find closest distance to volume, 0 means it's inside it
308+
float closestDistanceSqr = float.PositiveInfinity;
309+
310+
foreach (var collider in colliders)
304311
{
305-
// Skip disabled volumes and volumes without any data or weight
306-
if (!volume.enabled || volume.profileRef == null || volume.weight <= 0f)
312+
if (!collider.enabled)
307313
continue;
308314

309-
var settings = volume.profileRef.settings;
315+
var closestPoint = collider.ClosestPoint(triggerPos); // 5.6-only API
316+
var d = ((closestPoint - triggerPos) / 2f).sqrMagnitude;
310317

311-
// Global volume always have influence
312-
if (volume.isGlobal)
313-
{
314-
postProcessLayer.OverrideSettings(settings, Mathf.Clamp01(volume.weight));
315-
continue;
316-
}
318+
if (d < closestDistanceSqr)
319+
closestDistanceSqr = d;
320+
}
317321

318-
if (onlyGlobal)
319-
continue;
322+
colliders.Clear();
323+
float blendDistSqr = volume.blendDistance * volume.blendDistance;
320324

321-
// If volume isn't global and has no collider, skip it as it's useless
322-
var colliders = m_TempColliders;
323-
volume.GetComponents(colliders);
324-
if (colliders.Count == 0)
325-
continue;
325+
// Volume has no influence, ignore it
326+
// Note: Volume doesn't do anything when `closestDistanceSqr = blendDistSqr` but
327+
// we can't use a >= comparison as blendDistSqr could be set to 0 in which
328+
// case volume would have total influence
329+
if (closestDistanceSqr > blendDistSqr)
330+
continue;
326331

327-
// Find closest distance to volume, 0 means it's inside it
328-
float closestDistanceSqr = float.PositiveInfinity;
332+
// Volume has influence
333+
float interpFactor = 1f;
329334

330-
foreach (var collider in colliders)
331-
{
332-
if (!collider.enabled)
333-
continue;
335+
if (blendDistSqr > 0f)
336+
interpFactor = 1f - (closestDistanceSqr / blendDistSqr);
334337

335-
var closestPoint = collider.ClosestPoint(triggerPos); // 5.6-only API
336-
var d = ((closestPoint - triggerPos) / 2f).sqrMagnitude;
338+
// No need to clamp01 the interpolation factor as it'll always be in [0;1[ range
339+
postProcessLayer.OverrideSettings(settings, interpFactor * Mathf.Clamp01(volume.weight));
340+
}
341+
}
337342

338-
if (d < closestDistanceSqr)
339-
closestDistanceSqr = d;
340-
}
343+
List<PostProcessVolume> GrabVolumes(LayerMask mask)
344+
{
345+
List<PostProcessVolume> list;
341346

342-
colliders.Clear();
343-
float blendDistSqr = volume.blendDistance * volume.blendDistance;
347+
if (!m_SortedVolumes.TryGetValue(mask, out list))
348+
{
349+
// New layer mask detected, create a new list and cache all the volumes that belong
350+
// to this mask in it
351+
list = new List<PostProcessVolume>();
344352

345-
// Volume has no influence, ignore it
346-
// Note: Volume doesn't do anything when `closestDistanceSqr = blendDistSqr` but
347-
// we can't use a >= comparison as blendDistSqr could be set to 0 in which
348-
// case volume would have total influence
349-
if (closestDistanceSqr > blendDistSqr)
353+
foreach (var volume in m_Volumes)
354+
{
355+
if ((mask & (1 << volume.gameObject.layer)) == 0)
350356
continue;
351357

352-
// Volume has influence
353-
float interpFactor = 1f;
358+
list.Add(volume);
359+
m_SortNeeded[mask] = true;
360+
}
354361

355-
if (blendDistSqr > 0f)
356-
interpFactor = 1f - (closestDistanceSqr / blendDistSqr);
362+
m_SortedVolumes.Add(mask, list);
363+
}
357364

358-
// No need to clamp01 the interpolation factor as it'll always be in [0;1[ range
359-
postProcessLayer.OverrideSettings(settings, interpFactor * Mathf.Clamp01(volume.weight));
360-
}
365+
// Check sorting state
366+
bool sortNeeded;
367+
if (m_SortNeeded.TryGetValue(mask, out sortNeeded) && sortNeeded)
368+
{
369+
m_SortNeeded[mask] = false;
370+
SortByPriority(list);
361371
}
372+
373+
return list;
362374
}
363375

364376
// Custom insertion sort. First sort will be slower but after that it'll be faster than

0 commit comments

Comments
 (0)