Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ParameterCollection.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections;
5 using System.Collections.Generic;
6 using System.Diagnostics;
7 using System.Linq;
8 using System.Runtime.CompilerServices;
9 using System.Text;
10 using SiliconStudio.Core.Diagnostics;
11 using SiliconStudio.Core.ReferenceCounting;
12 using SiliconStudio.Core.Serialization;
13 using SiliconStudio.Core.Serialization.Converters;
14 using SiliconStudio.Core;
15 using SiliconStudio.Core.Collections;
16 using SiliconStudio.Core.Serialization.Serializers;
17 
18 namespace SiliconStudio.Paradox.Effects
19 {
20  /// <summary>
21  /// A container to handle a hierarchical collection of effect variables.
22  /// </summary>
23  [DataConverter(AutoGenerate = false)]
24  [DataSerializer(typeof(Data.ParameterCollectionSerializer))]
25  [DebuggerTypeProxy(typeof(CollectionDebugView))]
26  [DebuggerDisplay("Count = {Count}")]
27  [DataContract("!ParameterCollection")]
28  public class ParameterCollection : IParameterCollectionInheritanceInternal, IDictionary<ParameterKey, object>
29  {
30  // Internal values
31  internal FastListStruct<KeyValuePair<ParameterKey, InternalValue>> valueList;
32 
33  // Value ordered according to this.keys
34  internal InternalValue[] keys;
35 
36  // Either a ParameterCollection (inherits everything) or an InheritanceDefinition (inherits only specific key, ability to remap them as well)
37  private readonly List<IParameterCollectionInheritanceInternal> sources;
38 
39  // TODO: Maybe make this structure more simpler (second Dictionary is only here for event with ParameterKey == null mapping to all keys)
40  private Dictionary<ValueChangedEventKey, Dictionary<ParameterKey, InternalValueChangedDelegate>> valueChangedEvents;
41 
42  private List<ParameterDynamicValue> dynamicValues;
43 
44  // Match a specific ordering given by "keys" (especially useful for effects or components)
45  internal Dictionary<ParameterKey, int> keyMapping;
46 
47 
48  /// <summary>
49  /// Initializes a new instance of the <see cref="ParameterCollection"/> class.
50  /// </summary>
51  public ParameterCollection() : this(null)
52  {
53  }
54 
55  /// <summary>
56  /// Initializes a new instance of the <see cref="ParameterCollection"/> class.
57  /// </summary>
58  /// <param name="name">The name.</param>
59  public ParameterCollection(string name)
60  {
61  Name = name;
62  sources = new List<IParameterCollectionInheritanceInternal>();
63  valueList = new FastListStruct<KeyValuePair<ParameterKey, InternalValue>>(4);
64  }
65 
66  // Delegate definitions
67  internal delegate void OnUpdateValueDelegate(ParameterCollection source, ParameterKey key, InternalValue internalValue);
68  public delegate void ValueChangedDelegate(ParameterKey key, InternalValue internalValue, object oldValue);
69  internal delegate void InternalValueChangedDelegate(InternalValue internalValue, object oldValue);
70  internal event OnUpdateValueDelegate OnUpdateValue;
71  internal event OnUpdateValueDelegate OnInternalValueChange;
72 
73  public string Name { get; set; }
74 
75  /// <summary>
76  /// Gets the sources for this collection.
77  /// </summary>
78  public IParameterCollectionInheritance[] Sources
79  {
80  get { return sources.ToArray(); }
81  }
82 
83  /// <summary>
84  /// Gets the dynamic values.
85  /// </summary>
86  public IEnumerable<ParameterDynamicValue> DynamicValues
87  {
88  get
89  {
90  foreach (var source in Sources)
91  {
92  foreach (var dynamicValue in ((IParameterCollectionInheritanceInternal)source).DynamicValues)
93  {
94  // Ignore if defined in this collection (override)
95  if (dynamicValues == null || dynamicValues.All(x => x.Target != dynamicValue.Target))
96  yield return dynamicValue;
97  }
98  }
99 
100  if (dynamicValues != null)
101  {
102  foreach (var dynamicValue in dynamicValues)
103  yield return dynamicValue;
104  }
105  }
106  }
107 
108  void ICollection<KeyValuePair<ParameterKey, object>>.Add(KeyValuePair<ParameterKey, object> item)
109  {
110  SetObject(item.Key, item.Value);
111  }
112 
113  public void Clear()
114  {
115  // TODO: Proper clean that also propagate events to sources?
116  sources.Clear();
117  valueList.Clear();
118  if (dynamicValues != null)
119  dynamicValues.Clear();
120  if (valueChangedEvents != null)
121  valueChangedEvents.Clear();
122  keys = null;
123  }
124 
125  bool ICollection<KeyValuePair<ParameterKey, object>>.Contains(KeyValuePair<ParameterKey, object> item)
126  {
127  throw new NotImplementedException();
128  }
129 
130  void ICollection<KeyValuePair<ParameterKey, object>>.CopyTo(KeyValuePair<ParameterKey, object>[] array, int arrayIndex)
131  {
132  var keyvalues = valueList.Items.Select(x => new KeyValuePair<ParameterKey, object>(x.Key, x.Value.Object)).ToList();
133  keyvalues.CopyTo(array, arrayIndex);
134  }
135 
136  bool ICollection<KeyValuePair<ParameterKey, object>>.Remove(KeyValuePair<ParameterKey, object> item)
137  {
138  throw new NotImplementedException();
139  }
140 
141  /// <summary>
142  /// Gets the number of parameters stored in this instance..
143  /// </summary>
144  public int Count
145  {
146  get { return valueList.Count; }
147  }
148 
149  public bool IsReadOnly { get; private set; }
150 
151  /// <summary>
152  /// Gets the keys of this collection.
153  /// </summary>
155  {
156  get { return valueList.Select(x => x.Key); }
157  }
158 
159  public ICollection<object> Values { get; private set; }
160 
161  /// <summary>
162  /// Gets the number of internal values.
163  /// </summary>
164  internal int InternalCount
165  {
166  get { return valueList.Count; }
167  }
168 
169  /// <summary>
170  /// Gets the list of internal values.
171  /// </summary>
172  internal FastListStruct<KeyValuePair<ParameterKey, InternalValue>> InternalValues
173  {
174  get { return valueList; }
175  }
176 
177  /// <summary>
178  /// Adds an event that will be raised when a value is updated.
179  /// </summary>
180  /// <param name="key">Key to listen to (or null to listen to everything).</param>
181  /// <param name="valueUpdated">Delegate that will be called when value changes.</param>
182  public void AddEvent(ParameterKey key, ValueChangedDelegate valueUpdated)
183  {
184  if (valueChangedEvents == null)
185  valueChangedEvents = new Dictionary<ValueChangedEventKey, Dictionary<ParameterKey, InternalValueChangedDelegate>>();
186 
187  // check if the delegate was already added
188  var delegateKey = new ValueChangedEventKey(key, valueUpdated);
189  if (valueChangedEvents.ContainsKey(delegateKey))
190  return;
191 
192  valueChangedEvents.Add(delegateKey, new Dictionary<ParameterKey, InternalValueChangedDelegate>());
193 
194  if (key != null)
195  {
196  var internalValue = GetInternalValue(key);
197  if (internalValue != null)
198  UpdateValueChanged(key, internalValue, null);
199  }
200  else
201  {
202  foreach (var internalValue in valueList)
203  {
204  UpdateValueChanged(internalValue.Key, internalValue.Value, null);
205  }
206  }
207  }
208 
209  /// <summary>
210  /// Removes an event previously added with AddEvent.
211  /// </summary>
212  /// <param name="key"></param>
213  /// <param name="valueUpdated"></param>
214  internal void RemoveEvent(ParameterKey key, ValueChangedDelegate valueUpdated)
215  {
216  if (valueChangedEvents.ContainsKey(new ValueChangedEventKey(key, valueUpdated)))
217  {
218  if (key != null)
219  {
220  var internalValue = GetInternalValue(key);
221  if (internalValue != null)
222  UpdateValueChanged(key, null, internalValue);
223  }
224  else
225  {
226  foreach (var internalValue in valueList)
227  {
228  UpdateValueChanged(internalValue.Key, null, internalValue.Value);
229  }
230  }
231 
232  valueChangedEvents.Remove(new ValueChangedEventKey(key, valueUpdated));
233  }
234  }
235 
236  /// <summary>
237  /// Determines whether the specified instance contains a parameter key.
238  /// </summary>
239  /// <param name="key">The parameter key.</param>
240  /// <returns>
241  /// <c>true</c> if the specified key contains this key; otherwise, <c>false</c>.
242  /// </returns>
243  public bool ContainsKey(ParameterKey key)
244  {
245  int index;
246  return GetKeyIndex(key, out index);
247  }
248 
249  public void Add(ParameterKey key, object value)
250  {
251  SetObject(key, value);
252  }
253 
255  {
256  throw new NotImplementedException();
257  }
258 
259  public bool TryGetValue(ParameterKey key, out object value)
260  {
261  if (key == null) throw new ArgumentNullException("key");
262 
263  int index;
264  value = null;
265  if (!GetKeyIndex(key, out index))
266  {
267  return false;
268  }
269 
270  var internalValue = valueList.Items[index].Value;
271 
272  value = internalValue.Object;
273  return true;
274  }
275 
276  public object this[ParameterKey key]
277  {
278  get
279  {
280  return GetObject(key);
281  }
282  set
283  {
284  SetObject(key, value);
285  }
286  }
287 
289  {
290  get
291  {
292  return valueList.Items.Select(x => x.Key).ToList();
293  }
294  }
295 
296  IEnumerator IEnumerable.GetEnumerator()
297  {
298  return GetEnumerator();
299  }
300 
301  public IEnumerator<KeyValuePair<ParameterKey, object>> GetEnumerator()
302  {
303  return InternalValues.Select(x => new KeyValuePair<ParameterKey, object>(x.Key, x.Value.Object)).GetEnumerator();
304  }
305 
306  /// <inheritdoc/>
307  public override string ToString()
308  {
309  return string.Format("ParameterCollection [{0}]", Name);
310  }
311 
312  public string ToStringDetailed(string itemSeparator = "\r\n", string itemTab = " ")
313  {
314  var parameterCollectionText = new StringBuilder();
315  bool isNextParameter = false;
316  foreach (var paramValue in this)
317  {
318  if (isNextParameter)
319  {
320  parameterCollectionText.Append(itemSeparator);
321  }
322  parameterCollectionText.Append(itemTab).Append(paramValue.Key).Append(": ").Append(paramValue.Value);
323  isNextParameter = true;
324  }
325  return parameterCollectionText.ToString();
326  }
327 
328  public void Add<T>(ParameterKey<T> key, T value)
329  {
330  Set(key, value);
331  }
332 
333 
334  internal int GetKeyIndex(ParameterKey key)
335  {
336  int index = InternalValueBinarySearch(key);
337 
338  if (index < 0)
339  return -1;
340 
341  return index;
342  }
343 
344  protected bool GetKeyIndex(ParameterKey key, out int index)
345  {
346  index = InternalValueBinarySearch(key);
347 
348  if (index >= 0)
349  return true;
350 
351  index = -1;
352  return false;
353  }
354 
355  /// <summary>
356  /// Gets the index of an InternalValue within internalValues given its key.
357  /// </summary>
358  /// <param name="key"></param>
359  /// <returns></returns>
360 #if !SILICONSTUDIO_PLATFORM_IOS
361  // We can't inline on iOS temporarily due to https://bugzilla.xamarin.com/show_bug.cgi?id=17558
362  [MethodImpl(MethodImplOptions.AggressiveInlining)]
363 #endif
365  {
366  int start = 0;
367  int end = valueList.Count - 1;
368  while (start <= end)
369  {
370  int middle = start + ((end - start) >> 1);
371  var hash1 = valueList.Items[middle].Key.HashCode;
372  var hash2 = key.HashCode;
373 
374  if (hash1 == hash2)
375  {
376  return middle;
377  }
378  if (hash1 < hash2)
379  {
380  start = middle + 1;
381  }
382  else
383  {
384  end = middle - 1;
385  }
386  }
387  return ~start;
388  }
389 
390  /// <summary>
391  /// Gets or creates an internal value index given its key.
392  /// </summary>
393  /// <param name="key"></param>
394  /// <returns></returns>
396  {
397  int index = InternalValueBinarySearch(key);
398 
399  if (index < 0)
400  {
401  lock (sources)
402  {
403  index = ~index;
404  valueList.Insert(index, new KeyValuePair<ParameterKey, InternalValue>(key, null));
405  }
406  }
407 
408  return index;
409  }
410 
411  /// <summary>
412  /// Gets the value for the specified key.
413  /// </summary>
414  /// <typeparam name="T">A valuetype</typeparam>
415  /// <param name="key">The key.</param>
416  /// <returns>The value for the specified key</returns>
417  public T Get<T>(ParameterKey<T> key)
418  {
419  T result;
420  Get(key, out result);
421  return result;
422  }
423 
424  /// <summary>
425  /// Gets the value for the specified key.
426  /// </summary>
427  /// <typeparam name="T">A valuetype</typeparam>
428  /// <param name="key">The key.</param>
429  /// <param name="result">The result.</param>
430  public void Get<T>(ParameterKey<T> key, out T result)
431  {
432  if (key == null) throw new ArgumentNullException("key");
433 
434  int index;
435  if (!GetKeyIndex(key, out index))
436  {
437  result = key.DefaultMetadataT.DefaultValue;
438  return;
439  }
440 
441  GetValue(valueList.Items[index].Value, out result);
442  }
443 
444  public object GetObject(ParameterKey key)
445  {
446  if (key == null) throw new ArgumentNullException("key");
447 
448  int index;
449  if (!GetKeyIndex(key, out index))
450  {
451  return key.DefaultMetadata.GetDefaultValue();
452  }
453 
454  var internalValue = valueList.Items[index].Value;
455 
456  return internalValue.Object;
457  }
458 
459  internal void GetValue<T>(InternalValue internalValue, out T result)
460  {
461  result = ((InternalValueBase<T>)internalValue).Value;
462  }
463 
464  /// <summary>
465  /// Tries to get the value for the specified key.
466  /// </summary>
467  /// <typeparam name="T"></typeparam>
468  /// <param name="key">The key.</param>
469  /// <returns></returns>
470  public T TryGet<T>(ParameterKey<T> key)
471  {
472  T result;
473  TryGet(key, out result);
474  return result;
475  }
476 
477  /// <summary>
478  /// Tries to get the value for the specified key.
479  /// </summary>
480  /// <typeparam name="T"></typeparam>
481  /// <param name="key">The key.</param>
482  /// <param name="result">The result.</param>
483  /// <returns></returns>
484  public bool TryGet<T>(ParameterKey<T> key, out T result)
485  {
486  if (key == null) throw new ArgumentNullException("key");
487 
488  int index;
489  if (!GetKeyIndex(key, out index))
490  {
491  result = default(T);
492  return false;
493  }
494 
495  GetValue(valueList.Items[index].Value, out result);
496  return true;
497  }
498 
499  public void AddDynamic<T>(ParameterKey<T> key, ParameterDynamicValue<T> dynamicValue)
500  {
501  if (key == null) throw new ArgumentNullException("key");
502  if (dynamicValue == null) throw new ArgumentNullException("dynamicValue");
503 
504  SetDefault(key, true);
505  dynamicValue.Target = key;
506  if (dynamicValues == null)
507  dynamicValues = new List<ParameterDynamicValue>();
508  dynamicValues.Add(dynamicValue);
509  }
510 
511  /// <summary>
512  /// Sets the default value for the specified key (if undefined, otherwise do nothing).
513  /// </summary>
514  /// <param name="key">The key.</param>
515  /// <param name="overrideIfInherited">Specifies if inherited value should be overriden.</param>
516  public void SetDefault(ParameterKey key, bool overrideIfInherited = false)
517  {
518  if (key == null) throw new ArgumentNullException("key");
519 
520  bool newValue;
521  var index = GetOrCreateKeyIndex(key);
522  if (valueList.Items[index].Value != null && !overrideIfInherited)
523  return;
524 
525  GetOrCreateInternalValue(index, key, out newValue);
526 
527  if (newValue && OnUpdateValue != null) OnUpdateValue(this, key, valueList.Items[GetKeyIndex(key)].Value);
528  }
529 
530  /// <summary>
531  /// Sets a struct value for the specified key.
532  /// </summary>
533  /// <typeparam name="T">A valuetype</typeparam>
534  /// <param name="key">The key.</param>
535  /// <param name="value">The value.</param>
536  public void Set<T>(ParameterKey<T> key, T value)
537  {
538  if (key == null) throw new ArgumentNullException("key");
539 
540  bool newValue;
541  var index = GetOrCreateKeyIndex(key);
542  var internalValue = valueList.Items[index].Value;
543  object oldValue = (internalValue != null && internalValue.ValueChanged != null) ? internalValue.Object : null;
544  internalValue = GetOrCreateInternalValue(index, key, out newValue);
545 
546  ((InternalValueBase<T>)internalValue).Value = value;
547  internalValue.Counter++;
548 
549  if (newValue && OnUpdateValue != null) OnUpdateValue(this, key, internalValue);
550 
551  if (internalValue.ValueChanged != null)
552  internalValue.ValueChanged(internalValue, oldValue);
553  }
554 
555  /// <summary>
556  /// Sets an array of valuetypes for the specified key.
557  /// </summary>
558  /// <typeparam name="T">A valuetype</typeparam>
559  /// <param name="key">The key.</param>
560  /// <param name="values">The array of valuetypes.</param>
561  public void SetArray<T>(ParameterKey<T[]> key, T[] values) where T : struct
562  {
563  Set(key, values, 0, values.Length);
564  }
565 
566  /// <summary>
567  /// Sets an array of valuetypes for the specified key.
568  /// </summary>
569  /// <typeparam name="T">A valuetype</typeparam>
570  /// <param name="key">The key.</param>
571  /// <param name="values">The array of valuetypes.</param>
572  /// <param name="destinationOffset">The destination offset.</param>
573  /// <param name="count">The number of elements to copy from value array.</param>
574  public void Set<T>(ParameterKey<T[]> key, T[] values, int destinationOffset, int count) where T : struct
575  {
576  if (key == null) throw new ArgumentNullException("key");
577 
578  bool newValue;
579  var index = GetOrCreateKeyIndex(key);
580  var internalValue = (InternalValueArray<T>)valueList.Items[index].Value;
581  object oldValue = (internalValue != null && internalValue.ValueChanged != null) ? internalValue.Object : null;
582  internalValue = (InternalValueArray<T>)GetOrCreateInternalValue(index, key, out newValue);
583 
584  // First use with a null default value? (happen with variable size array)
585  if (internalValue.Value == null || internalValue.Value.Length < count)
586  {
587  if (destinationOffset != 0)
588  throw new InvalidOperationException("Should use destinationOffset 0 and real count for first set if no default value.");
589  internalValue.Value = new T[count];
590  }
591 
592  for (int i = 0; i < count; ++i)
593  ((InternalValueArray<T>)internalValue).Value[destinationOffset + i] = values[i];
594  internalValue.Counter++;
595 
596  if (newValue && OnUpdateValue != null) OnUpdateValue(this, key, internalValue);
597 
598  if (internalValue.ValueChanged != null)
599  internalValue.ValueChanged(internalValue, oldValue);
600  }
601 
602  /// <summary>
603  /// Sets an array of valuetypes for the specified key.
604  /// </summary>
605  /// <typeparam name="T">A valuetype</typeparam>
606  /// <param name="key">The key.</param>
607  /// <param name="values">The array of valuetypes.</param>
608  /// <param name="sourceOffset">The source offset.</param>
609  /// <param name="destinationOffset">The destination offset.</param>
610  /// <param name="count">The number of elements to copy from value array.</param>
611  public void Set<T>(ParameterKey<T[]> key, T[] values, int sourceOffset, int destinationOffset, int count) where T : struct
612  {
613  if (key == null) throw new ArgumentNullException("key");
614 
615  bool newValue;
616  var index = GetOrCreateKeyIndex(key);
617  var internalValue = (InternalValueArray<T>)valueList.Items[index].Value;
618  object oldValue = (internalValue != null && internalValue.ValueChanged != null) ? internalValue.Object : null;
619  internalValue = (InternalValueArray<T>)GetOrCreateInternalValue(index, key, out newValue);
620 
621  // First use with a null default value? (happen with variable size array)
622  if (internalValue.Value == null || internalValue.Value.Length < count)
623  {
624  if (destinationOffset != 0)
625  throw new InvalidOperationException("Should use destinationOffset 0 and real count for first set if no default value.");
626  internalValue.Value = new T[count];
627  }
628 
629  for (int i = 0; i < count; ++i)
630  ((InternalValueArray<T>)internalValue).Value[destinationOffset + i] = values[sourceOffset + i];
631  internalValue.Counter++;
632 
633  if (newValue && OnUpdateValue != null) OnUpdateValue(this, key, internalValue);
634 
635  if (internalValue.ValueChanged != null)
636  internalValue.ValueChanged(internalValue, oldValue);
637  }
638 
639  public void SetObject(ParameterKey key, object resourceValue)
640  {
641  if (key == null) throw new ArgumentNullException("key");
642 
643  bool newValue;
644  var index = GetOrCreateKeyIndex(key);
645  var internalValue = valueList.Items[index].Value;
646  object oldValue = (internalValue != null && internalValue.ValueChanged != null) ? internalValue.Object : null;
647  internalValue = GetOrCreateInternalValue(index, key, out newValue);
648 
649  internalValue.Object = resourceValue;
650  internalValue.Counter++;
651 
652  if (newValue && OnUpdateValue != null) OnUpdateValue(this, key, internalValue);
653 
654  if (internalValue.ValueChanged != null)
655  internalValue.ValueChanged(internalValue, oldValue);
656  }
657 
658  /// <summary>
659  /// Removes the specified key and associated value
660  /// </summary>
661  /// <param name="key">The key.</param>
662  /// <exception cref="InvalidOperationException">If trying to remove a key from a collection that is not the owner. Or trying to remove a key that is referenced by a dynamic key</exception>
663  public void Remove(ParameterKey key)
664  {
665  lock (sources)
666  {
667  int index = GetKeyIndex(key); //mapKeyToIndex[key]);
668  if (index < 0) return;
669  var internalValue = valueList.Items[index].Value;
670  ReleaseValue(valueList.Items[index].Key, valueList.Items[index].Value);
671  valueList.Items[index] = new KeyValuePair<ParameterKey, InternalValue>(key, null);
672  //mapKeyToIndex.Remove(key);
673  valueList.RemoveAt(index);
674  //internalValues = valueList.Items;
675  OnKeyUpdate(key, null, internalValue);
676 
677  // TODO: Should try to inherit this value from another collection (if present)
678 
679  if (OnUpdateValue != null) OnUpdateValue(this, key, null);
680  }
681  }
682 
683  /// <summary>
684  /// Tests if the values in parameters are contained into this instance.
685  /// It will automatically handle default values as well (that is, if something is set in parameters with default value but not set in this instance, it will be ignored).
686  /// </summary>
687  /// <param name="parameters">The collection of parameters that should be included in this one.</param>
688  /// <returns>True if this collection contains all values from parameters. False otherwise.</returns>
689  public bool Contains(ParameterCollection parameters)
690  {
691  // TODO: Possible optimization: iterate on both sorted collections?
692  foreach (var internalValue2 in parameters.InternalValues)
693  {
694  var internalValue1 = GetInternalValue(internalValue2.Key);
695 
696  // If parameter is not in parameter 1, we still allow it if it was a default value
697  // Defer actual test to InternalValue override (avoid boxing)
698  if (internalValue1 == null && internalValue2.Value.IsDefaultValue(internalValue2.Key))
699  continue;
700 
701  // Otherwise, values must match
702  // Defer actual test to InternalValue override (avoid boxing)
703  if (!internalValue2.Value.Equals(internalValue1))
704  return false;
705  }
706 
707  return true;
708  }
709 
710  /// <summary>
711  /// Adds the sources.
712  /// </summary>
713  /// <param name="parameterCollections">The effect variable collections.</param>
714  public void AddSources(params IParameterCollectionInheritance[] newSources)
715  {
716  //if (OnUpdateValue != null)
717  // throw new NotSupportedException("Adding sources to ParameterCollection which are inherited is not supported yet.");
718 
719  // TODO: Check for multiple inheritance
720 
721  var oldSources = sources.ToArray();
722  foreach (IParameterCollectionInheritanceInternal source in newSources)
723  {
724  if (sources.Contains(source))
725  continue;
726 
727  // Add the new source collection
728  sources.Add(source);
729  }
730 
731  UpdateSources(oldSources);
732 
733  if (OnUpdateValue != null) OnUpdateValue(this, null, null);
734 
735  lock (sources)
736  {
737  // Iterate on new hierarchy
738  for (int i = oldSources.Length; i < sources.Count; ++i)
739  {
740  valueList.EnsureCapacity(sources[i].GetInternalValueCount());
741 
742  // Iterate on each keys
743  foreach (var sourceInternalValue in sources[i].GetInternalValues())
744  {
745  var key = sourceInternalValue.Key;
746 
747  var localIndex = GetKeyIndex(key);
748 
749  if (localIndex != -1)
750  {
751  if (FindOverrideGroupIndex(valueList.Items[localIndex]) >= FindOverrideGroupIndex(sourceInternalValue))
752  continue;
753  }
754 
755  InheritValue(sourceInternalValue.Value, key);
756  localIndex = GetKeyIndex(key);
757  if (OnUpdateValue != null) OnUpdateValue(this, key, valueList.Items[localIndex].Value);
758  }
759  }
760  }
761  }
762 
763  /// <summary>
764  /// Removes the sources.
765  /// </summary>
766  /// <param name="parameterCollection">The source parameter collection.</param>
767  public bool RemoveSource(IParameterCollectionInheritance removedInheritance)
768  {
769  var oldSources = sources.ToArray();
770 
771  var internalValueSources = valueList.Select(x =>
772  {
773  var sourceIndex = FindOverrideGroupIndex(x);
774  return sourceIndex == sources.Count ? null : sources[sourceIndex];
775  }).ToArray();
776 
777  if (!sources.Remove((IParameterCollectionInheritanceInternal)removedInheritance))
778  return false;
779 
780  UpdateSources(oldSources);
781  if (OnUpdateValue != null) OnUpdateValue(this, null, null);
782 
783  var removedSources = oldSources.Except(sources).ToArray();
784 
785  lock (sources)
786  {
787  for (int index = 0, index2 = 0; index < this.InternalCount; ++index, ++index2)
788  {
789  var internalValue = valueList[index];
790  var key = internalValue.Key;
791  var source = internalValueSources[index2];
792  if (source != null && removedSources.Contains(source))
793  {
794  // TODO: Inherit from another value (if any)
795  valueList.RemoveAt(index--);
796  if (OnUpdateValue != null) OnUpdateValue(this, key, null);
797  OnKeyUpdate(key, null, internalValue.Value);
798  }
799  }
800  }
801 
802  return true;
803  }
804 
805  /// <summary>
806  /// Removes the value locally and try to get a value from a source.
807  /// </summary>
808  /// <param name="key">The key.</param>
809  public void Reset(ParameterKey key)
810  {
811  int index = GetKeyIndex(key);
812  if (index == -1)
813  return;
814 
815  // If overriden in a source, inherits it
816  for (int i = sources.Count - 1; i >= 0; --i)
817  {
818  var source = sources[i];
819  var internalValue = source.GetInternalValue(key);
820  if (internalValue != null)
821  {
822  InheritValue(internalValue, key);
823  return;
824  }
825  }
826 
827  // Otherwise, simply remove it
828  var oldInternalValue = valueList.Items[index].Value;
829  valueList.RemoveAt(index);
830 
831  // Notify InternalValue change
832  OnKeyUpdate(key, null, oldInternalValue);
833  }
834 
835  /// <summary>
836  /// Determines whether [is value owner] of [the specified key].
837  /// </summary>
838  /// <param name="key">The key.</param>
839  /// <returns>
840  /// <c>true</c> if [is value owner] of [the specified key]; otherwise, <c>false</c>.
841  /// </returns>
842  public bool IsValueOwner(InternalValue internalValue)
843  {
844  if (internalValue != null)
845  return internalValue.Owner == this;
846  return false;
847  }
848 
849  /// <inheritdoc/>
850  protected void Destroy()
851  {
852  //base.Destroy();
853 
854  if (OnUpdateValue != null)
855  throw new InvalidOperationException("Cannot dispose a parameter collection that is used as a source.");
856 
857  // Unsubscribes from all sources
858  for (int i = 0; i < sources.Count; ++i)
859  {
860  var parameterCollection = sources[i].GetParameterCollection();
861  var updateValueDelegate = sources[i].GetUpdateValueDelegate(effectVariableCollection_OnUpdateValue);
862  parameterCollection.OnUpdateValue -= updateValueDelegate;
863  }
864 
865  for (int i = 0; i < InternalCount; i++)
866  {
867  ReleaseValue(valueList.Items[i].Key, valueList.Items[i].Value);
868  }
869  }
870 
871  /// <summary>
872  /// Create an internal value given its ParameterKey.
873  /// </summary>
874  /// <param name="key"></param>
875  /// <returns></returns>
876  internal static InternalValue CreateInternalValue(ParameterKey key)
877  {
878  return key.CreateInternalValue();
879  }
880 
881  private object GetResource(InternalValue internalValue)
882  {
883  return internalValue.Object;
884  }
885 
886  private void OnKeyUpdate(ParameterKey key, InternalValue internalValue, InternalValue oldValue)
887  {
888  UpdateValueChanged(key, internalValue, oldValue);
889  UpdateKeyMapping(key, internalValue);
890  }
891 
892  /// <summary>
893  /// Inherits an InternalValue.
894  /// </summary>
895  /// <param name="internalValue"></param>
896  /// <param name="key"></param>
897  private void InheritValue(InternalValue internalValue, ParameterKey key)
898  {
899  int index = GetKeyIndex(key);
900  var oldInternalValue = index != -1 ? valueList.Items[index].Value : null;
901 
902  // Copy the InternalValue in this ParameterCollection
903  index = GetOrCreateKeyIndex(key);
904  valueList.Items[index] = new KeyValuePair<ParameterKey, InternalValue>(key, internalValue);
905 
906  // Notify InternalValue change
907  OnKeyUpdate(key, internalValue, oldInternalValue);
908  }
909 
910  /// <summary>
911  /// Updates OnUpdateValue delegate for newly added/removed sources.
912  /// </summary>
913  /// <param name="oldSources"></param>
914  private void UpdateSources(IParameterCollectionInheritance[] oldSources)
915  {
916  foreach (IParameterCollectionInheritanceInternal source in oldSources)
917  {
918  if (sources.Contains(source))
919  continue;
920  var parameterCollection = source.GetParameterCollection();
921  var updateValueDelegate = source.GetUpdateValueDelegate(effectVariableCollection_OnUpdateValue);
922  parameterCollection.OnUpdateValue -= updateValueDelegate;
923  }
924 
925  foreach (IParameterCollectionInheritanceInternal source in sources)
926  {
927  if (oldSources.Contains(source))
928  continue;
929  var parameterCollection = source.GetParameterCollection();
930  var updateValueDelegate = source.GetUpdateValueDelegate(effectVariableCollection_OnUpdateValue);
931  parameterCollection.OnUpdateValue += updateValueDelegate;
932  }
933  }
934 
935  /// <summary>
936  /// Returns index in flattened hierarchy if positive, otherwise sources.Count (this) or -1 (not found).
937  /// </summary>
938  /// <param name="internalValue"></param>
939  /// <returns></returns>
940  private int FindOverrideGroupIndex(KeyValuePair<ParameterKey, InternalValue> internalValue)
941  {
942  if (internalValue.Value.Owner == this)
943  return sources.Count;
944 
945  // Fast lookup
946  if (internalValue.Value.Owner != null)
947  {
948  int flattenedIndex = sources.IndexOf(internalValue.Value.Owner);
949  if (flattenedIndex != -1)
950  return flattenedIndex;
951  }
952 
953  // Otherwise check values
954  for (int i = sources.Count - 1; i >= 0; --i)
955  {
956  var source = sources[i];
957  if (source.GetInternalValues().Any(x => x.Value == internalValue.Value))
958  {
959  return i;
960  }
961  }
962 
963  return -1;
964  }
965 
966  /// <summary>
967  /// Called when an InternalValue has been updated.
968  /// It will recursively notify dependents ParameterCollection as well.
969  /// </summary>
970  private void effectVariableCollection_OnUpdateValue(ParameterCollection source, ParameterKey key, InternalValue sourceInternalValue)
971  {
972  effectVariableCollection_OnUpdateValueLocal(source, key, sourceInternalValue);
973  if (OnUpdateValue != null)
974  OnUpdateValue(this, key, sourceInternalValue);
975  }
976 
977  private void effectVariableCollection_OnUpdateValueLocal(ParameterCollection source, ParameterKey key, InternalValue sourceInternalValue)
978  {
979  // Sources changed
980  if (key == null)
981  {
982  return;
983  }
984 
985  var sourceIndex = sources.IndexOf(source);
986 
987  if (sourceInternalValue == null)
988  {
989  int currentIndex = GetKeyIndex(key);
990  if (currentIndex == -1)
991  return;
992 
993  var currentSourceIndex = FindOverrideGroupIndex(valueList.Items[currentIndex]);
994 
995  if (currentSourceIndex > sourceIndex && currentSourceIndex != -1)
996  return;
997 
998  // Deleted key
999  // First, check if another inherited value is still available
1000  for (int i = sourceIndex - 1; i >= 0; --i)
1001  {
1002  var newSource = sources[i];
1003  var internalValue = newSource.GetInternalValue(key);
1004  if (internalValue != null)
1005  {
1006  InheritValue(internalValue, key);
1007  return;
1008  }
1009  }
1010 
1011  // Otherwise simply remove it
1012  Remove(key);
1013  return;
1014  }
1015 
1016  var sourceKey = key; //sourceInternalValue.Key;
1017  var index = GetKeyIndex(sourceKey);
1018 
1019  if (index != -1)
1020  {
1021  // We already have a value, check if this one is a better override
1022  var currentValueSourceIndex = FindOverrideGroupIndex(valueList.Items[index]);
1023 
1024  if (sourceIndex >= currentValueSourceIndex) // || currentValueSourceIndex == -1)
1025  {
1026  InheritValue(sourceInternalValue, sourceKey);
1027  }
1028  }
1029  else
1030  {
1031  // New key
1032  InheritValue(sourceInternalValue, sourceKey);
1033  }
1034  }
1035 
1036  /// <summary>
1037  /// Updates ValueChanged event for newly changed InternalValue.
1038  /// </summary>
1039  /// <param name="key"></param>
1040  /// <param name="newInternalValue"></param>
1041  /// <param name="oldInternalValue"></param>
1042  internal void UpdateValueChanged(ParameterKey key, InternalValue newInternalValue, InternalValue oldInternalValue)
1043  {
1044  if (valueChangedEvents != null)
1045  {
1046  foreach (var valueChangedEvent in valueChangedEvents)
1047  {
1048  if (valueChangedEvent.Key.Key == key || valueChangedEvent.Key.Key == null)
1049  {
1050  var events = valueChangedEvent.Value;
1051  InternalValueChangedDelegate internalEvent;
1052  if (!events.TryGetValue(key, out internalEvent))
1053  {
1054  var originalEvent = valueChangedEvent.Key.ValueChanged;
1055  internalEvent = CreateInternalValueChangedEvent(key, internalEvent, originalEvent);
1056  events.Add(key, internalEvent);
1057  }
1058  if (oldInternalValue != null)
1059  oldInternalValue.ValueChanged -= internalEvent;
1060  if (newInternalValue != null)
1061  newInternalValue.ValueChanged += internalEvent;
1062  }
1063  }
1064  }
1065  }
1066 
1067  private static InternalValueChangedDelegate CreateInternalValueChangedEvent(ParameterKey key, InternalValueChangedDelegate internalEvent, ValueChangedDelegate originalEvent)
1068  {
1069  internalEvent = (internalValue, oldValue) => originalEvent(key, internalValue, oldValue);
1070  return internalEvent;
1071  }
1072 
1073  #region Key mapping
1074 
1075  /// <summary>
1076  /// Get InternalValue at given index of key mapping specified with SetKeyMapping.
1077  /// </summary>
1078  /// <param name="index"></param>
1079  /// <returns></returns>
1080  internal InternalValue GetUpdatedInternalValue(int index)
1081  {
1082  return keys[index];
1083  }
1084 
1085  internal T GetResource<T>(int index)
1086  {
1087  return (T)GetResource(keys[index]);
1088  }
1089 
1090  /// <summary>
1091  /// Called when any InternalValue is updated so that key mapping is kept updated.
1092  /// </summary>
1093  /// <param name="key"></param>
1094  /// <param name="internalValue"></param>
1095  private void UpdateKeyMapping(ParameterKey key, InternalValue internalValue)
1096  {
1097  int index;
1098 
1099  if (keyMapping != null && keyMapping.TryGetValue(key, out index))
1100  {
1101  keys[index] = internalValue;
1102  if (OnInternalValueChange != null)
1103  OnInternalValueChange(this, key, internalValue);
1104  }
1105  }
1106 
1107  /// <summary>
1108  /// Sets a specific key mapping, which can then be used when querying for InternalValue with GetUpdatedInternalValue(index).
1109  /// It allows for skipping key lookup when performance is required (i.e. in rendering code).
1110  /// </summary>
1111  /// <param name="keyMapping"></param>
1112  internal void SetKeyMapping(Dictionary<ParameterKey, int> keyMapping)
1113  {
1114  this.keyMapping = keyMapping;
1115  this.keys = new InternalValue[keyMapping.Count];
1116  foreach (var internalValue in valueList)
1117  {
1118  UpdateKeyMapping(internalValue.Key, internalValue.Value);
1119  }
1120  }
1121 
1122  #endregion
1123 
1124  /// <summary>
1125  /// Releases this InternalValue and its associated data.
1126  /// </summary>
1127  /// <param name="key"></param>
1128  /// <param name="internalValue"></param>
1129  private void ReleaseValue(ParameterKey key, InternalValue internalValue)
1130  {
1131  if (internalValue == null)
1132  return;
1133 
1134  if (!key.IsValueType && internalValue.Owner == this)
1135  {
1136  internalValue.Object = null;
1137  }
1138  }
1139 
1140  /// <summary>
1141  /// Gets internal value from specificed key.
1142  /// </summary>
1143  /// <param name="key"></param>
1144  /// <returns></returns>
1145  internal InternalValue GetInternalValue(ParameterKey key)
1146  {
1147  int index = GetKeyIndex(key);
1148  if (index == -1)
1149  return null;
1150 
1151  return valueList.Items[index].Value;
1152  }
1153 
1154  /// <summary>
1155  /// Gets or creates an internal value given its index and key.
1156  /// </summary>
1157  /// <param name="index"></param>
1158  /// <param name="key"></param>
1159  /// <param name="newValue"></param>
1160  /// <returns></returns>
1161  private InternalValue GetOrCreateInternalValue(int index, ParameterKey key, out bool newValue)
1162  {
1163  var oldInternalValue = valueList.Items[index];
1164  var internalValue = oldInternalValue;
1165  newValue = false;
1166 
1167  if (internalValue.Value != null && internalValue.Value.Owner != this)
1168  {
1169  internalValue = new KeyValuePair<ParameterKey, InternalValue>(internalValue.Key, null);
1170  }
1171  if (internalValue.Value == null)
1172  {
1173  newValue = true;
1174  valueList.Items[index] = internalValue = new KeyValuePair<ParameterKey, InternalValue>(key, CreateInternalValue(key));
1175  internalValue.Value.Owner = this;
1176 
1177  OnKeyUpdate(key, internalValue.Value, oldInternalValue.Value);
1178  }
1179  return internalValue.Value;
1180  }
1181 
1182  #region Implements IParameterCollectionInheritanceInternal
1183 
1184  int IParameterCollectionInheritanceInternal.GetInternalValueCount()
1185  {
1186  return InternalCount;
1187  }
1188 
1189  InternalValue IParameterCollectionInheritanceInternal.GetInternalValue(ParameterKey key)
1190  {
1191  return GetInternalValue(key);
1192  }
1193 
1194  IEnumerable<KeyValuePair<ParameterKey, InternalValue>> IParameterCollectionInheritanceInternal.GetInternalValues()
1195  {
1196  return InternalValues;
1197  }
1198 
1199  ParameterCollection IParameterCollectionInheritanceInternal.GetParameterCollection()
1200  {
1201  return this;
1202  }
1203 
1204  OnUpdateValueDelegate IParameterCollectionInheritanceInternal.GetUpdateValueDelegate(ParameterCollection.OnUpdateValueDelegate original)
1205  {
1206  return original;
1207  }
1208 
1209  #endregion
1210 
1211  /// <summary>
1212  /// Dynamic values use this class when pointing to a source.
1213  /// </summary>
1214  internal struct InternalValueReference
1215  {
1216  public InternalValue Entry;
1217  public int Counter;
1218  }
1219 
1220  /// <summary>
1221  /// Holds a value inside ParameterCollection.
1222  /// </summary>
1223  public abstract class InternalValue
1224  {
1225  public int Counter;
1227  internal InternalValueChangedDelegate ValueChanged;
1228  internal InternalValueReference[] Dependencies;
1229 
1230  public virtual void ReadFrom(IntPtr dest, int offset, int size)
1231  {
1232  throw new NotImplementedException();
1233  }
1234 
1235  public virtual object Object
1236  {
1237  get { return null; }
1238  set { throw new NotImplementedException(); }
1239  }
1240 
1241  public abstract void SerializeHash(SerializationStream stream);
1242 
1243  /// <summary>
1244  /// Determines if this instance and the given internal value have same <see cref="Value"/>.
1245  /// </summary>
1246  /// <param name="internalValue">The internal value.</param>
1247  /// <returns></returns>
1248  public abstract bool Equals(InternalValue internalValue);
1249 
1250  /// <summary>
1251  /// Determines whether [is default value] [the specified parameter key].
1252  /// </summary>
1253  /// <param name="parameterKey">The parameter key.</param>
1254  /// <returns></returns>
1255  public abstract bool IsDefaultValue(ParameterKey parameterKey);
1256 
1257  public override string ToString()
1258  {
1259  var builder = new StringBuilder();
1260  builder.AppendFormat("({0}) {1} Count {2}", Owner.Name, Object, Counter);
1261  return builder.ToString();
1262  }
1263  }
1264 
1265  /// <summary>
1266  /// Holds a value of a specific type in a ParameterCollection.
1267  /// </summary>
1268  /// <typeparam name="T"></typeparam>
1269  internal class InternalValueBase<T> : InternalValue
1270  {
1271  private static EqualityComparer<T> comparer = EqualityComparer<T>.Default;
1272  private static DataSerializer<T> dataSerializer;
1273 
1274  public T Value;
1275 
1276  public override object Object
1277  {
1278  get { return Value; }
1279  set { Value = (T)value; }
1280  }
1281 
1282  public override bool IsDefaultValue(ParameterKey parameterKey)
1283  {
1284  var parameterKeyT = parameterKey as ParameterKey<T>;
1285  if (parameterKeyT == null)
1286  return false;
1287 
1288  return comparer.Equals(Value, parameterKeyT.DefaultMetadataT.DefaultValue);
1289  }
1290 
1291  public override bool Equals(InternalValue internalValue)
1292  {
1293  var internalValueT = internalValue as InternalValueBase<T>;
1294  if (internalValueT == null)
1295  return false;
1296 
1297  return comparer.Equals(Value, internalValueT.Value);
1298  }
1299 
1300  public override void SerializeHash(SerializationStream stream)
1301  {
1302  var currentDataSerializer = dataSerializer;
1303  if (currentDataSerializer == null)
1304  {
1305  dataSerializer = currentDataSerializer = MemberSerializer<T>.Create(stream.Context.SerializerSelector);
1306  }
1307 
1308  currentDataSerializer.Serialize(ref Value, ArchiveMode.Serialize, stream);
1309  }
1310 
1311  protected void SetValue(T value)
1312  {
1313  Value = value;
1314  }
1315 
1316  protected T GetValue()
1317  {
1318  return Value;
1319  }
1320 
1321  }
1322 
1323  internal class InternalValue<T> : InternalValueBase<T>
1324  {
1325  public override unsafe void ReadFrom(IntPtr dest, int offset, int size)
1326  {
1327  if (offset == 0 && size == Utilities.UnsafeSizeOf<T>())
1328  {
1329  Utilities.UnsafeWrite(dest, ref Value);
1330  }
1331  else
1332  {
1333  Utilities.CopyMemory(dest, (IntPtr)Interop.Fixed(ref Value) + offset, size);
1334  }
1335  }
1336 
1337  }
1338 
1339  internal class InternalValueArray<T> : InternalValueBase<T[]>
1340  {
1341  private int elementSize;
1342 
1343  public InternalValueArray(int length)
1344  {
1345  elementSize = Utilities.UnsafeSizeOf<T>();
1346  if (length != -1)
1347  {
1348  // TODO Workaround for obfuscation. Cannoy set base.Value directly here
1349  SetValue(new T[length]);
1350  }
1351  }
1352 
1353  public override unsafe void ReadFrom(IntPtr dest, int offset, int size)
1354  {
1355  if (GetValue() == null)
1356  return;
1357 
1358  // Compute the maximum copyable size so that we don't go out of bounds.
1359  int maxSize = (elementSize * GetValue().Length - offset);
1360  if (maxSize <= 0)
1361  return;
1362  Utilities.CopyMemory(dest, (IntPtr)Interop.Fixed(ref GetValue()[0]) + offset, Math.Min(size, maxSize));
1363  }
1364  }
1365 
1366  private struct ValueChangedEventKey : IEquatable<ValueChangedEventKey>
1367  {
1368  public ParameterKey Key;
1369  public ValueChangedDelegate ValueChanged;
1370 
1371  public ValueChangedEventKey(ParameterKey key, ValueChangedDelegate valueChanged)
1372  {
1373  Key = key;
1374  ValueChanged = valueChanged;
1375  }
1376 
1377  public bool Equals(ValueChangedEventKey other)
1378  {
1379  return Key.Equals(other.Key) && ValueChanged.Equals(other.ValueChanged);
1380  }
1381 
1382  public override bool Equals(object obj)
1383  {
1384  if (ReferenceEquals(null, obj)) return false;
1385  return obj is ValueChangedEventKey && Equals((ValueChangedEventKey)obj);
1386  }
1387 
1388  public override int GetHashCode()
1389  {
1390  unchecked
1391  {
1392  return (Key.GetHashCode()*397) ^ ValueChanged.GetHashCode();
1393  }
1394  }
1395  }
1396  }
1397 }
Key of an effect parameter.
Definition: ParameterKey.cs:15
ParameterCollection()
Initializes a new instance of the ParameterCollection class.
IEnumerator< KeyValuePair< ParameterKey, object > > GetEnumerator()
Base for inheriting ParameterCollection (used by ParameterCollection.AddSources). This allows for dir...
bool TryGetValue(ParameterKey key, out object value)
void AddSources(params IParameterCollectionInheritance[] newSources)
Adds the sources.
bool RemoveSource(IParameterCollectionInheritance removedInheritance)
Removes the sources.
Base class for converters to/from a data type.
Key of an gereric effect parameter.
int Length
Gets the number of elements for this key.
Definition: ParameterKey.cs:40
void AddEvent(ParameterKey key, ValueChangedDelegate valueUpdated)
Adds an event that will be raised when a value is updated.
SiliconStudio.Paradox.Input.Keys Keys
string ToStringDetailed(string itemSeparator="\r\n", string itemTab=" ")
bool ContainsKey(ParameterKey key)
Determines whether the specified instance contains a parameter key.
void Remove(ParameterKey key)
Removes the specified key and associated value
void SetObject(ParameterKey key, object resourceValue)
_In_ size_t count
Definition: DirectXTexP.h:174
void SetDefault(ParameterKey key, bool overrideIfInherited=false)
Sets the default value for the specified key (if undefined, otherwise do nothing).
int GetOrCreateKeyIndex(ParameterKey key)
Gets or creates an internal value index given its key.
Base class for implementation of SerializationStream.
virtual void ReadFrom(IntPtr dest, int offset, int size)
Helper for serializing members of a class.
bool IsValueOwner(InternalValue internalValue)
Determines whether [is value owner] of [the specified key].
bool Contains(ParameterCollection parameters)
Tests if the values in parameters are contained into this instance. It will automatically handle defa...
ParameterCollection(string name)
Initializes a new instance of the ParameterCollection class.
Use this class to provide a debug output in Visual Studio debugger.
The remove mixin to remove a mixin from current mixins.
int InternalValueBinarySearch(ParameterKey key)
Gets the index of an InternalValue within internalValues given its key.
Describes how to serialize and deserialize an object without knowing its type. Used as a common base ...
char * dest
Definition: lz4.h:61
Describes how to serialize and deserialize an object of a given type.
bool GetKeyIndex(ParameterKey key, out int index)
_In_ size_t _In_ size_t size
Definition: DirectXTexP.h:175
void Reset(ParameterKey key)
Removes the value locally and try to get a value from a source.
A container to handle a hierarchical collection of effect variables.