@@ -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