Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ShaderMixinContext.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.Generic;
5 using System.Linq;
6 using System.Reflection;
7 
8 using SiliconStudio.Core;
9 using SiliconStudio.Paradox.Effects;
10 
11 namespace SiliconStudio.Paradox.Shaders
12 {
13  /// <summary>
14  /// A context used when mixin <see cref="ShaderSource"/>.
15  /// </summary>
16  public class ShaderMixinContext
17  {
18  private readonly ParameterCollection defaultPropertyContainer;
19  private readonly Stack<ParameterCollection> propertyContainers = new Stack<ParameterCollection>();
20  private readonly Dictionary<string, IShaderMixinBuilder> registeredBuilders;
21  private readonly Dictionary<object, object> strippedPropertyContainers = new Dictionary<object, object>();
22  private readonly Stack<ShaderMixinParameters> currentUsedParameters = new Stack<ShaderMixinParameters>();
23  private readonly List<ShaderMixinParameters> finalUsedParameters = new List<ShaderMixinParameters>();
24  private readonly Stack<string> shaderNames = new Stack<string>();
25  private readonly Stack<HashSet<ParameterKey>> blackListKeys = new Stack<HashSet<ParameterKey>>();
26  private readonly Stack<ParameterCollection> currentPropertyContainers = new Stack<ParameterCollection>();
27 
28  /// <summary>
29  /// Initializes a new instance of the <see cref="ShaderMixinContext"/> class.
30  /// </summary>
31  /// <param name="defaultPropertyContainer">The default property container.</param>
32  /// <param name="registeredBuilders">The registered builders.</param>
33  /// <param name="shaderBaseName">The name of the base shader.</param>
34  /// <exception cref="System.ArgumentNullException">
35  /// defaultPropertyContainer
36  /// or
37  /// registeredBuilders
38  /// </exception>
39  public ShaderMixinContext(ParameterCollection defaultPropertyContainer, Dictionary<string, IShaderMixinBuilder> registeredBuilders, string shaderBaseName)
40  {
41  if (defaultPropertyContainer == null)
42  throw new ArgumentNullException("defaultPropertyContainer");
43 
44  if (registeredBuilders == null)
45  throw new ArgumentNullException("registeredBuilders");
46 
47  // TODO: use a copy of the defaultPropertyContainer?
48  this.defaultPropertyContainer = defaultPropertyContainer;
49  this.registeredBuilders = registeredBuilders;
50 
51  strippedPropertyContainers.Add(defaultPropertyContainer, new ShaderMixinParameters());
52 
53  currentUsedParameters.Push(new ShaderMixinParameters(shaderBaseName));
54  shaderNames.Push(shaderBaseName);
55  blackListKeys.Push(new HashSet<ParameterKey>());
56  currentPropertyContainers.Push(defaultPropertyContainer);
57  }
58 
59  /// <summary>
60  /// Pushes the current parameters collection being used.
61  /// </summary>
62  /// <typeparam name="T">Type of the parameter collection</typeparam>
63  /// <param name="propertyContainer">The property container.</param>
64  public void PushParameters<T>(T propertyContainer) where T : ParameterCollection
65  {
66  propertyContainers.Push(propertyContainer);
67  }
68 
69  /// <summary>
70  /// Pops the parameters collection.
71  /// </summary>
72  public void PopParameters()
73  {
74  propertyContainers.Pop();
75  }
76 
77  /// <summary>
78  /// Gets a parameter value for the specified key.
79  /// </summary>
80  /// <typeparam name="T">Type of the parameter value</typeparam>
81  /// <param name="key">The key.</param>
82  /// <returns>The value or default value associated to this parameter key.</returns>
83  /// <exception cref="System.ArgumentNullException">key</exception>
84  public T GetParam<T>(ParameterKey<T> key)
85  {
86  if (key == null)
87  throw new ArgumentNullException("key");
88 
89  ParameterCollection sourcePropertyContainer = null;
90 
91  T value;
92  // Try to get a value from registered containers
93  foreach (var propertyContainer in propertyContainers)
94  {
95  if (propertyContainer.TryGet(key, out value))
96  {
97  sourcePropertyContainer = propertyContainer;
98  goto valueFound; // Use goto to speedup the code and avoid usage of additionnal bool state
99  }
100  }
101 
102  // Else gets the value (or default value) from the default property container
103  sourcePropertyContainer = defaultPropertyContainer;
104  value = currentPropertyContainers.Peek().Get(key);
105  // do not store the keys behind a ParameterKey<ShaderMixinParameters>, only when it comes from defaultPropertyContainer
106  if (!blackListKeys.Peek().Contains(key))
107  currentUsedParameters.Peek().Set(key, value);
108 
109  valueFound:
110 
111  // Cache the strip property container
112  var stripPropertyContainer = (ParameterCollection)ReplicateContainer(sourcePropertyContainer);
113 
114  if (!stripPropertyContainer.ContainsKey(key))
115  {
116  var stripValue = value;
117  if (IsPropertyContainer(value))
118  {
119  stripValue = (T)ReplicateContainer(value);
120  }
121  stripPropertyContainer.Set(key, stripValue);
122  }
123 
124  return value;
125  }
126 
127  /// <summary>
128  /// Gets all parameters used by this context when mixin a shader.
129  /// </summary>
130  /// <returns>ShaderMixinParameters.</returns>
132  {
133  return (ShaderMixinParameters)strippedPropertyContainers[defaultPropertyContainer];
134  }
135 
136  public List<ShaderMixinParameters> GetUsedParameters()
137  {
138  while (currentUsedParameters.Count > 0)
139  finalUsedParameters.Add(currentUsedParameters.Pop());
140  return finalUsedParameters;
141  }
142 
143  private bool IsPropertyContainer(object source)
144  {
145  return source is PropertyContainer || source is PropertyContainer[];
146  }
147 
148  private object ReplicateContainer(object source)
149  {
150  object objectToReplicate = null;
151  if (!strippedPropertyContainers.TryGetValue(source, out objectToReplicate))
152  {
153  if (source is ParameterCollection)
154  {
155  objectToReplicate = new ShaderMixinParameters();
156  }
157  else
158  {
159  var containers = source as ParameterCollection[];
160  if (containers != null)
161  {
162  var subPropertyContainers = new ShaderMixinParameters[containers.Length];
163  for (int i = 0; i < containers.Length; i++)
164  {
165  subPropertyContainers[i] = (ShaderMixinParameters)ReplicateContainer(containers[i]);
166  }
167  objectToReplicate = containers;
168  }
169  }
170 
171  strippedPropertyContainers.Add(source, objectToReplicate);
172  }
173  return objectToReplicate;
174  }
175 
176  public void SetParam<T>(ParameterKey<T> key, T value)
177  {
178  if (key == null)
179  throw new ArgumentNullException("key");
180 
181  var propertyContainer = propertyContainers.Count > 0 ? propertyContainers.Peek() : currentPropertyContainers.Peek();
182  propertyContainer.Set(key, value);
183 
184  if (propertyContainers.Count == 0) // in currentDefaultContainer?
185  blackListKeys.Peek().Add(key);
186  }
187 
188  /// <summary>
189  /// Removes the specified mixin from this instance.
190  /// </summary>
191  /// <param name="mixinTree">The mixin tree.</param>
192  /// <param name="name">The name.</param>
193  public void RemoveMixin(ShaderMixinSourceTree mixinTree, string name)
194  {
195  var mixinParent = mixinTree.Mixin;
196  for (int i = mixinParent.Mixins.Count - 1; i >= 0; i--)
197  {
198  var mixin = mixinParent.Mixins[i];
199  if (mixin.ClassName == name)
200  {
201  mixinParent.Mixins.RemoveAt(i);
202  }
203  }
204  }
205 
206  /// <summary>
207  /// Mixins a <see cref="ShaderClassSource"/> identified by its name/generic parameters into the specified mixin tree.
208  /// </summary>
209  /// <param name="mixinTree">The mixin tree.</param>
210  /// <param name="name">The name.</param>
211  /// <param name="genericParameters">The generic parameters.</param>
212  /// <exception cref="System.InvalidOperationException">If the class source doesn't support generic parameters</exception>
213  public void Mixin(ShaderMixinSourceTree mixinTree, string name, params object[] genericParameters)
214  {
215  IShaderMixinBuilder builder;
216  if (!registeredBuilders.TryGetValue(name, out builder))
217  {
218  // Else simply add the name of the shader
219  mixinTree.Mixin.Mixins.Add(new ShaderClassSource(name, genericParameters));
220  }
221 
222  if (builder != null)
223  {
224  if (genericParameters.Length != 0)
225  {
226  throw new InvalidOperationException(string.Format("Generic Parameters are not supported with [{0}]", builder.GetType().GetTypeInfo().Name));
227  }
228  builder.Generate(mixinTree, this);
229  }
230  }
231 
232  /// <summary>
233  /// Creates a new ParameterCollection for a child shader.
234  /// </summary>
235  public void BeginChild(ShaderMixinSourceTree subMixin)
236  {
237  var parentName = shaderNames.Peek();
238  var childName = parentName + "." + subMixin.Name;
239  currentUsedParameters.Push(new ShaderMixinParameters(childName));
240  shaderNames.Push(childName);
241 
242  // copy the keys that have been set
243  var blk = new HashSet<ParameterKey>();
244  foreach (var blackKey in blackListKeys.Peek())
245  blk.Add(blackKey);
246  blackListKeys.Push(blk);
247 
248  // copy the set of parameters
249  var pc = new ParameterCollection();
250  currentPropertyContainers.Peek().CopyTo(pc);
251  currentPropertyContainers.Push(pc);
252  }
253 
254  /// <summary>
255  /// Copy the properties of the parent to the calling clone.
256  /// </summary>
257  public void CloneProperties()
258  {
259  if (currentUsedParameters.Count > 1)
260  {
261  var childUsedParameters = currentUsedParameters.Pop();
262  var parentUsedParameters = currentUsedParameters.Peek();
263  parentUsedParameters.CopyTo(childUsedParameters);
264  currentUsedParameters.Push(childUsedParameters);
265  }
266  }
267 
268  /// <summary>
269  /// Ends the computation of the child mixin and store the used parameters.
270  /// </summary>
271  public void EndChild()
272  {
273  shaderNames.Pop();
274  blackListKeys.Pop();
275  currentPropertyContainers.Pop();
276  finalUsedParameters.Add(currentUsedParameters.Pop());
277  }
278 
279  /// <summary>
280  /// Mixins a <see cref="ShaderMixinSource"/> into the specified mixin tree.
281  /// </summary>
282  /// <param name="mixinTree">The mixin tree.</param>
283  /// <param name="shaderMixinSource">The shader mixin source.</param>
284  public void Mixin(ShaderMixinSourceTree mixinTree, ShaderMixinSource shaderMixinSource)
285  {
286  mixinTree.Mixin.CloneFrom(shaderMixinSource);
287  }
288  }
289 }
A mixin performing a combination of ShaderClassSource and other mixins.
ShaderMixinContext(ParameterCollection defaultPropertyContainer, Dictionary< string, IShaderMixinBuilder > registeredBuilders, string shaderBaseName)
Initializes a new instance of the ShaderMixinContext class.
Represents a container that can hold properties, lightweight to embed (lazy initialized).
A context used when mixin ShaderSource.
Key of an gereric effect parameter.
void Mixin(ShaderMixinSourceTree mixinTree, ShaderMixinSource shaderMixinSource)
Mixins a ShaderMixinSource into the specified mixin tree.
List< ShaderMixinParameters > GetUsedParameters()
void BeginChild(ShaderMixinSourceTree subMixin)
Creates a new ParameterCollection for a child shader.
ShaderMixinParameters GetMainUsedParameters()
Gets all parameters used by this context when mixin a shader.
void Mixin(ShaderMixinSourceTree mixinTree, string name, params object[] genericParameters)
Mixins a ShaderClassSource identified by its name/generic parameters into the specified mixin tree...
void PopParameters()
Pops the parameters collection.
void EndChild()
Ends the computation of the child mixin and store the used parameters.
void CloneProperties()
Copy the properties of the parent to the calling clone.
Interface to be implemented for dynamic mixin generation.
void RemoveMixin(ShaderMixinSourceTree mixinTree, string name)
Removes the specified mixin from this instance.
A container to handle a hierarchical collection of effect variables.