Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ParadoxShaderMixer.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 
7 using SiliconStudio.Paradox.Shaders.Parser.Analysis;
8 using SiliconStudio.Paradox.Shaders.Parser.Ast;
9 using SiliconStudio.Paradox.Shaders.Parser.Utility;
10 using SiliconStudio.Shaders.Ast;
11 using SiliconStudio.Shaders.Ast.Hlsl;
12 using SiliconStudio.Shaders.Utility;
13 
15 
16 namespace SiliconStudio.Paradox.Shaders.Parser.Mixins
17 {
18  internal class ParadoxShaderMixer
19  {
20  private readonly static string FlipRendertargetVariableName = "ParadoxFlipRendertarget";
21 
22  #region Public members
23 
24  /// <summary>
25  /// The final shader
26  /// </summary>
27  public ShaderClassType MixedShader = null;
28 
29  /// <summary>
30  /// Log of all the warnings and errors
31  /// </summary>
32  private readonly LoggerResult log;
33 
34  #endregion
35 
36  #region Private members
37 
38  /// <summary>
39  /// the module to generate
40  /// </summary>
41  private ModuleMixin mainModuleMixin = null;
42 
43  /// <summary>
44  /// the extern modules
45  /// </summary>
46  private Dictionary<Variable, List<ModuleMixin>> CompositionsPerVariable;
47 
48  /// <summary>
49  /// List of all the method Declaration
50  /// </summary>
51  private HashSet<List<MethodDeclaration>> StageMethodInheritance = new HashSet<List<MethodDeclaration>>();
52 
53  /// <summary>
54  /// Ordered list of all the mixin in their appearance order
55  /// </summary>
56  private List<ModuleMixin> MixinInheritance = new List<ModuleMixin>();
57 
58  /// <summary>
59  /// Dictionary of all the mixins used for this compilation
60  /// </summary>
61  private Dictionary<string, ModuleMixin> mixContext;
62 
63  /// <summary>
64  /// The default clone context
65  /// </summary>
66  private CloneContext defaultCloneContext;
67 
68  #endregion
69 
70  #region Constructor
71 
72  /// <summary>
73  /// Constructor
74  /// </summary>
75  /// <param name="moduleMixin">the final shader information</param>
76  /// <param name="log">The log.</param>
77  /// <param name="context">all the mixins in the context</param>
78  /// <param name="compositionsPerVariable">The compositions per variable.</param>
79  /// <param name="cloneContext">The clone context.</param>
80  /// <exception cref="System.ArgumentNullException">
81  /// moduleMixin
82  /// or
83  /// log
84  /// or
85  /// context
86  /// </exception>
87  public ParadoxShaderMixer(ModuleMixin moduleMixin, LoggerResult log, Dictionary<string, ModuleMixin> context, Dictionary<Variable, List<ModuleMixin>> compositionsPerVariable, CloneContext cloneContext = null)
88  {
89  if (moduleMixin == null)
90  throw new ArgumentNullException("moduleMixin");
91 
92  if (log == null)
93  throw new ArgumentNullException("log");
94 
95  if (context == null)
96  throw new ArgumentNullException("context");
97 
98  this.log = log;
99 
100  mixContext = context;
101  mainModuleMixin = moduleMixin;
102  defaultCloneContext = cloneContext;
103 
104  if (compositionsPerVariable != null)
105  CompositionsPerVariable = compositionsPerVariable;
106  else
107  CompositionsPerVariable = new Dictionary<Variable, List<ModuleMixin>>();
108 
109  var mixinsToAnalyze = new Stack<ModuleMixin>(CompositionsPerVariable.SelectMany(x => x.Value));
110  mixinsToAnalyze.Push(mainModuleMixin);
111 
112  while (mixinsToAnalyze.Count > 0)
113  AddDefaultCompositions(mixinsToAnalyze);
114  }
115 
116  #endregion
117 
118  #region Public methods
119 
120 /// <summary>
121  /// Performs the mix
122  /// </summary>
123  public void Mix()
124  {
125  CreateReferencesStructures();
126 
127  mainModuleMixin.ClassReferences.RegenKeys();
128  mainModuleMixin.ExternReferences.RegenKeys();
129  mainModuleMixin.StaticReferences.RegenKeys();
130  mainModuleMixin.StageInitReferences.RegenKeys();
131 
132  foreach (var externMix in CompositionsPerVariable.SelectMany(externMixes => externMixes.Value))
133  {
134  externMix.ClassReferences.RegenKeys();
135  externMix.ExternReferences.RegenKeys();
136  externMix.StaticReferences.RegenKeys();
137  externMix.StageInitReferences.RegenKeys();
138  }
139 
140  BuildMixinInheritance(mainModuleMixin);
141  MixinInheritance = MixinInheritance.Distinct().ToList();
142 
143  ComputeMixinOccurence();
144  BuildStageInheritance();
145 
146  LinkVariables(mainModuleMixin, "", new List<ModuleMixin>());
147  ProcessExterns();
148 
149  // patch the base/this functions
150  PatchAllMethodInferences(mainModuleMixin);
151 
152  MergeReferences();
153 
154  // then everything in the inheritance
155  RenameAllVariables();
156  RenameAllMethods();
157 
158  // group into one AST
159  GenerateShader();
160  }
161 
162  public Shader GetMixedShader()
163  {
164  var shader = new Shader();
165  shader.Declarations.AddRange(MixedShader.Members);
166 
167  return shader;
168  }
169 
170  #endregion
171 
172  #region Private methods
173 
174  /// <summary>
175  /// Add default compositions if no already present
176  /// </summary>
177  /// <param name="mixinsToAnalyze">the remaining mixins to analyzez</param>
178  private void AddDefaultCompositions(Stack<ModuleMixin> mixinsToAnalyze)
179  {
180  var nextMixin = mixinsToAnalyze.Pop();
181 
182  foreach (var externVar in nextMixin.VariableDependencies)
183  {
184  if (!CompositionsPerVariable.ContainsKey(externVar.Key))
185  {
186  var newComp = externVar.Value.DeepClone(defaultCloneContext);
187  mixinsToAnalyze.Push(newComp);
188  CompositionsPerVariable.Add(externVar.Key, new List<ModuleMixin> { newComp });
189  }
190  }
191  foreach (var dep in nextMixin.InheritanceList)
192  mixinsToAnalyze.Push(dep);
193  }
194 
195  /// <summary>
196  /// performs semantic analysis on mixin that have composition arrays
197  /// </summary>
198  private void RedoSematicAnalysis()
199  {
200  // first: assign the size of the array
201  foreach (var composition in CompositionsPerVariable.Where(x => x.Key.Type is ArrayType))
202  {
203  var arrayType = composition.Key.Type as ArrayType;
204  if (arrayType.Dimensions.Count > 1)
205  {
206  log.Error(ParadoxMessageCode.ErrorMultidimensionalCompositionArray, arrayType.Span, arrayType, composition.Value.First().MixinName);
207  return;
208  }
209  arrayType.Dimensions[0] = new LiteralExpression(composition.Value.Count);
210  }
211 
212  // then rerun the semantic analysis
213  foreach (var composition in CompositionsPerVariable.Where(x => x.Key.Type is ArrayType))
214  {
215  var moduleMixin = GetTopMixin(composition.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin);
216  var compilationContext = moduleMixin.MinimalContext.Where(x => !(moduleMixin.InheritanceList.Contains(x) || x == moduleMixin)).ToList();
217 
218  // rerun the semantic analysis in all the shader that inherits from the one where the composition was declared.
219  foreach (var inheritedMixin in moduleMixin.InheritanceList)
220  inheritedMixin.ParsingInfo = ParadoxSemanticAnalysis.RunAnalysis(inheritedMixin, compilationContext, true);
221  moduleMixin.ParsingInfo = ParadoxSemanticAnalysis.RunAnalysis(moduleMixin, compilationContext, true);
222 
223  if (moduleMixin.ParsingInfo.ErrorsWarnings.HasErrors)
224  return;
225  //throw new Exception("Semantic analysis failed in ParadoxShaderMixer");
226  }
227  }
228 
229  /// <summary>
230  /// Create the references for each top mixin
231  /// </summary>
232  private void CreateReferencesStructures()
233  {
234  RedoSematicAnalysis();
235  CreateReferencesStructures(mainModuleMixin);
236  foreach (var compositions in CompositionsPerVariable)
237  {
238  foreach (var comp in compositions.Value)
239  CreateReferencesStructures(comp);
240  }
241  }
242 
243  /// <summary>
244  /// Merge reference from mixin dependencies
245  /// </summary>
246  /// <param name="mixin"></param>
247  private void CreateReferencesStructures(ModuleMixin mixin)
248  {
249  GetStaticReferences(mixin, mixin);
250 
251  // merge class reference
252  mixin.ClassReferences.Merge(mixin.ParsingInfo.ClassReferences);
253  foreach (var dep in mixin.InheritanceList)
254  mixin.ClassReferences.Merge(dep.ParsingInfo.ClassReferences);
255  // merge static references
256  mixin.StaticReferences.Merge(mixin.ParsingInfo.StaticReferences);
257  foreach (var dep in mixin.InheritanceList)
258  mixin.StaticReferences.Merge(dep.ParsingInfo.StaticReferences);
259  // merge extern references
260  mixin.ExternReferences.Merge(mixin.ParsingInfo.ExternReferences);
261  foreach (var dep in mixin.InheritanceList)
262  mixin.ExternReferences.Merge(dep.ParsingInfo.ExternReferences);
263  // merge stage init references
264  mixin.StageInitReferences.Merge(mixin.ParsingInfo.StageInitReferences);
265  foreach (var dep in mixin.InheritanceList)
266  mixin.StageInitReferences.Merge(dep.ParsingInfo.StageInitReferences);
267  }
268 
269  /// <summary>
270  /// bubble up the static references in the mixin dependency tree
271  /// </summary>
272  /// <param name="topMixin">the top mixin</param>
273  /// <param name="staticMixin">the mixin to look into</param>
274  private void GetStaticReferences(ModuleMixin topMixin, ModuleMixin staticMixin)
275  {
276  foreach (var staticDep in staticMixin.ParsingInfo.StaticClasses)
277  GetStaticReferences(topMixin, staticDep);
278  foreach (var staticDep in staticMixin.InheritanceList)
279  GetStaticReferences(topMixin, staticDep);
280 
281  topMixin.StaticReferences.Merge(staticMixin.ParsingInfo.StaticReferences);
282  }
283 
284  /// <summary>
285  /// Rename the links of the variables
286  /// </summary>
287  /// <param name="mixin">the current mixin</param>
288  /// <param name="context">the string to append</param>
289  /// <param name="visitedMixins">list of already visited mixin</param>
290  private void LinkVariables(ModuleMixin mixin, string context, List<ModuleMixin> visitedMixins)
291  {
292  if (visitedMixins.Contains(mixin))
293  return;
294 
295  visitedMixins.Add(mixin);
296 
297  foreach (var variable in mixin.LocalVirtualTable.Variables.Select(x => x.Variable))
298  {
299  if (variable.Qualifiers.Contains(ParadoxStorageQualifier.Extern))
300  {
301  var baselink = context + "." + variable.Name.Text;
302  List<ModuleMixin> mixins;
303  if (CompositionsPerVariable.TryGetValue(variable, out mixins))
304  {
305  if (variable.Type is ArrayType)
306  {
307  for (var i = 0; i < mixins.Count; ++i)
308  {
309  LinkVariables(mixins[i], baselink + "[" + i + "]", visitedMixins);
310  }
311  }
312  else
313  {
314  LinkVariables(mixins[0], baselink, visitedMixins);
315  }
316  }
317  }
318 
319  if (!(variable.Qualifiers.Values.Contains(ParadoxStorageQualifier.Stream)
320  || variable.Qualifiers.Values.Contains(ParadoxStorageQualifier.PatchStream)
321  || variable.Qualifiers.Values.Contains(ParadoxStorageQualifier.Extern)))
322  {
323  var attribute = variable.Attributes.OfType<AttributeDeclaration>().FirstOrDefault(x => x.Name == "Link");
324  if (attribute == null)
325  {
326  // Try to get class name before generics
327  //string baseClassName;
328  //if (!genericTypeDefinitions.TryGetValue(baseClass, out baseClassName))
329  // baseClassName = baseClass.Name;
330 
331  // TODO: class name before renaming if generics
332  string linkName;
333 
334  // Use Map attribute (if it exists)
335  var mapAttribute = variable.Attributes.OfType<AttributeDeclaration>().FirstOrDefault(x => x.Name == "Map");
336  if (mapAttribute != null)
337  {
338  linkName = (string)mapAttribute.Parameters[0].Value;
339  // Remove "Keys" from class name (or maybe we should just include it in key name to avoid issues?)
340  linkName = linkName.Replace("Keys.", ".");
341  }
342  else
343  {
344  linkName = mixin.MixinGenericName + "." + variable.Name.Text;
345  }
346 
347  attribute = new AttributeDeclaration { Name = new Identifier("Link"), Parameters = new List<Literal> { new Literal(linkName) } };
348  variable.Attributes.Add(attribute);
349  }
350 
351  // Append location to key in case it is a local variable
352  if (!variable.Qualifiers.Values.Contains(ParadoxStorageQualifier.Stage))
353  {
354  attribute.Parameters[0].SubLiterals = null; // set to null to avoid conflict with the member Value
355  attribute.Parameters[0].Value = (string)attribute.Parameters[0].Value + context;
356  }
357  }
358  }
359 
360  foreach (var variable in mixin.StaticReferences.VariablesReferences.Select(x => x.Key))
361  {
362  var attribute = variable.Attributes.OfType<AttributeDeclaration>().FirstOrDefault(x => x.Name == "Link");
363  if (attribute == null)
364  {
365  var baseClassName = (variable.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinGenericName;
366 
367  attribute = new AttributeDeclaration { Name = new Identifier("Link"), Parameters = new List<Literal> { new Literal(baseClassName + "." + variable.Name.Text) } };
368  variable.Attributes.Add(attribute);
369  }
370  }
371 
372  mixin.InheritanceList.ForEach(x => LinkVariables(x, context, visitedMixins));
373  }
374 
375  /// <summary>
376  /// Merge the class references of the externs to the main class, and the static calls too
377  /// </summary>
378  private void MergeReferences()
379  {
380  foreach (var externMixes in CompositionsPerVariable)
381  {
382  foreach (var externMix in externMixes.Value)
383  mainModuleMixin.ClassReferences.Merge(externMix.ClassReferences);
384  }
385 
386  mainModuleMixin.ClassReferences.Merge(mainModuleMixin.StaticReferences);
387  }
388 
389  /// <summary>
390  /// Add the stage variables from the mixin to the main one
391  /// </summary>
392  /// <param name="mixin">the ModuleMixin</param>
393  private void AddStageVariables(ModuleMixin mixin)
394  {
395  mixin.InheritanceList.ForEach(AddStageVariables);
396  CompositionsPerVariable.Where(x => mixin.LocalVirtualTable.Variables.Any(y => y.Variable == x.Key)).ToList().ForEach(externMixes => externMixes.Value.ForEach(AddStageVariables));
397 
398  foreach (var variable in mixin.LocalVirtualTable.Variables)
399  {
400  if (variable.Variable.Qualifiers.Contains(ParadoxStorageQualifier.Stage))
401  {
402  var shaderName = variable.Shader.Name.Text;
403  var sameVar = mainModuleMixin.ClassReferences.VariablesReferences.FirstOrDefault(x => x.Key.Name.Text == variable.Variable.Name.Text && (x.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName == shaderName).Key;
404  if (sameVar != null)
405  continue;
406  }
407  if (!mainModuleMixin.ClassReferences.VariablesReferences.ContainsKey(variable.Variable))
408  mainModuleMixin.ClassReferences.VariablesReferences.Add(variable.Variable, new HashSet<ExpressionNodeCouple>());
409  }
410  }
411 
412  /// <summary>
413  /// Build an ordered list of mixin defining the inheritance for stage values
414  /// </summary>
415  /// <param name="mixin">the mixin to add</param>
416  private void BuildMixinInheritance(ModuleMixin mixin)
417  {
418  mixin.InheritanceList.ForEach(BuildMixinInheritance);
419  MixinInheritance.Add(mixin);
420  CompositionsPerVariable.Where(x => mixin.LocalVirtualTable.Variables.Any(y => y.Variable == x.Key)).ToList().ForEach(externMixes => externMixes.Value.ForEach(BuildMixinInheritance));
421  }
422 
423  /// <summary>
424  /// Compute the occurence Id of each mixin
425  /// </summary>
426  private void ComputeMixinOccurence()
427  {
428  foreach (var mixin in MixinInheritance)
429  {
430  foreach (var mixin2 in MixinInheritance)
431  {
432  if (mixin.MixinName == mixin2.MixinName)
433  ++(mixin.OccurenceId);
434  if (mixin == mixin2)
435  break;
436  }
437  }
438  }
439 
440  /// <summary>
441  /// Find the correct variable inference
442  /// </summary>
443  /// <param name="expression"></param>
444  /// <param name="mixin"></param>
445  /// <returns></returns>
446  private Variable FindVariable(Expression expression, ref ModuleMixin mixin)
447  {
448  Variable result = null;
449  var index = 0;
450  if (expression is VariableReferenceExpression)
451  {
452  result = FindVariableInMixin((expression as VariableReferenceExpression).Name.Text, mixin);
453  }
454  else if (expression is MemberReferenceExpression)
455  {
456  var memberExpression = expression as MemberReferenceExpression;
457  var target = memberExpression.Target;
458 
459  if (target.TypeInference.Declaration is Variable)
460  FindVariable(target, ref mixin);
461  else if (target.TypeInference.Declaration is ShaderClassType || target.TypeInference.TargetType is ShaderClassType)
462  FindShader(target, ref mixin);
463 
464  result = FindVariableInMixin(memberExpression.Member.Text, mixin);
465  }
466  else if (expression is IndexerExpression)
467  {
468  var indexerExpression = expression as IndexerExpression;
469  var target = indexerExpression.Target;
470 
471  if (target.TypeInference.Declaration is Variable)
472  result = FindVariable(target, ref mixin);
473 
474  index = (int)(indexerExpression.Index as LiteralExpression).Value;
475  }
476 
477  if (result is Variable && (result as Variable).Qualifiers.Contains(ParadoxStorageQualifier.Extern) && !((result as Variable).Type is ArrayType))
478  mixin = CompositionsPerVariable[result as Variable][index];
479 
480  return result;
481  }
482 
483  private Variable FindVariableInMixin(string varName, ModuleMixin mixin)
484  {
485  if (varName == "streams")
486  return null;
487 
488  var foundVar = mixin.VirtualTable.Variables.FirstOrDefault(x => x.Variable.Name.Text == varName);
489  if (foundVar != null)
490  return foundVar.Variable;
491 
492  log.Error(ParadoxMessageCode.ErrorVariableNotFound, new SourceSpan(), varName, mixin.MixinName);
493  return null;
494  }
495 
496  private MethodDeclaration FindMethod(Expression expression, ref ModuleMixin mixin)
497  {
498  if (expression is MemberReferenceExpression)
499  {
500  var memberExpression = expression as MemberReferenceExpression;
501  var target = memberExpression.Target;
502 
503  if (target.TypeInference.Declaration is Variable)
504  FindVariable(target, ref mixin);
505  else if (target.TypeInference.Declaration is ShaderClassType || target.TypeInference.TargetType is ShaderClassType)
506  FindShader(target, ref mixin);
507  }
508 
509  var topMixin = GetTopMixin(mixin);
510  if (topMixin == null)
511  {
512  log.Error(ParadoxMessageCode.ErrorTopMixinNotFound, expression.Span, expression);
513  return null;
514  }
515  var foundMethod = topMixin.GetMethodFromExpression(expression);
516  if (foundMethod == null)
517  {
518  log.Error(ParadoxMessageCode.ErrorCallNotFound, expression.Span, expression);
519  return null;
520  }
521  if (foundMethod.Qualifiers.Contains(ParadoxStorageQualifier.Abstract))
522  {
523  log.Error(ParadoxMessageCode.ErrorCallToAbstractMethod, expression.Span, expression, foundMethod);
524  return null;
525  }
526  return foundMethod;
527  }
528 
529  private void FindShader(Expression expression, ref ModuleMixin mixin)
530  {
531  if (expression is MemberReferenceExpression)
532  {
533  var memberExpression = expression as MemberReferenceExpression;
534  var target = memberExpression.Target;
535 
536  if (target.TypeInference.Declaration is Variable)
537  FindVariable(target, ref mixin);
538 
539  var mixinName = (expression.TypeInference.Declaration as ShaderClassType).Name.Text;
540  mixin = mixin.MixinName == mixinName ? mixin : mixin.InheritanceList.FirstOrDefault(x => x.MixinName == mixinName);
541  }
542  else if (expression is IndexerExpression)
543  {
544  var indexerExpression = expression as IndexerExpression;
545  var target = indexerExpression.Target;
546 
547  Variable result = null;
548 
549  if (target.TypeInference.Declaration is Variable)
550  result = FindVariable(target, ref mixin);
551 
552  var index = (int)(indexerExpression.Index as LiteralExpression).Value;
553  if (result is Variable && (result as Variable).Qualifiers.Contains(ParadoxStorageQualifier.Extern))
554  mixin = CompositionsPerVariable[result as Variable][index];
555  }
556  }
557 
558  /// <summary>
559  /// Build inheritance list for methods
560  /// </summary>
561  private void BuildStageInheritance()
562  {
563  foreach (var mixin in MixinInheritance)
564  InsertStageMethods(mixin.LocalVirtualTable.Methods.Select(x => x.Method).Where(x => x.Qualifiers.Values.Contains(ParadoxStorageQualifier.Stage)).ToList(), GetTopMixin(mixin));
565  }
566 
567  /// <summary>
568  /// Adds le methods in the list to the inheritance list
569  /// </summary>
570  /// <param name="extMethodList">the list of methods</param>
571  /// <param name="mixin">the mixin in which the methods are defined</param>
572  public void InsertStageMethods(List<MethodDeclaration> extMethodList, ModuleMixin mixin)
573  {
574  foreach (var extMethod in extMethodList)
575  {
576  if (extMethod is MethodDefinition)
577  {
578  var isClone = extMethod.Qualifiers.Values.Contains(ParadoxStorageQualifier.Clone);
579  var newEntry = true;
580 
581  // find a corresponding method
582  var vtReference = mixin.VirtualTable.GetBaseDeclaration(extMethod);
583  foreach (var stageMethodList in StageMethodInheritance)
584  {
585  if (!newEntry)
586  break;
587 
588  if (stageMethodList == null || stageMethodList.Count == 0)
589  continue;
590 
591  var firstOccurence = stageMethodList.First();
592  var occurenceMixin = firstOccurence.GetTag(ParadoxTags.ShaderScope) as ModuleMixin;
593  var listVTReference = occurenceMixin.VirtualTable.GetBaseDeclaration(firstOccurence);
594 
595  if (vtReference.Slot != listVTReference.Slot || vtReference.Shader != listVTReference.Shader)
596  continue;
597 
598  newEntry = false;
599  var extMixin = extMethod.GetTag(ParadoxTags.ShaderScope) as ModuleMixin;
600  if (isClone || extMixin.OccurenceId == 1)
601  stageMethodList.Add(extMethod);
602  }
603 
604  if (newEntry)
605  {
606  var list = new List<MethodDeclaration>();
607  list.Add(extMethod);
608  StageMethodInheritance.Add(list);
609  }
610 
611  var externClassRef = GetTopMixin(mixin).ClassReferences;
612  if (externClassRef != null && !mainModuleMixin.ClassReferences.MethodsReferences.ContainsKey(extMethod))
613  {
614  externClassRef.RegenKeys();
615  mainModuleMixin.ClassReferences.MethodsReferences.Add(extMethod, externClassRef.MethodsReferences[extMethod]);
616  externClassRef.MethodsReferences.Remove(extMethod);
617  }
618  }
619  }
620  }
621 
622  /// <summary>
623  /// Add the method to its correct dictionary
624  /// </summary>
625  /// <param name="expression"></param>
626  private void AddToMethodsReferences(MethodInvocationExpression expression)
627  {
628  var decl = expression.Target.TypeInference.Declaration as MethodDeclaration;
629  if (decl != null)
630  {
631  if (!mainModuleMixin.ClassReferences.MethodsReferences.ContainsKey(decl))
632  mainModuleMixin.ClassReferences.MethodsReferences.Add(decl, new HashSet<MethodInvocationExpression>());
633  mainModuleMixin.ClassReferences.MethodsReferences[decl].Add(expression);
634  }
635  }
636 
637  /// <summary>
638  /// Remove the method from the correctdictionary
639  /// </summary>
640  /// <param name="expression"></param>
641  private void RemoveFromMethodsReferences(MethodInvocationExpression expression, ModuleMixin mixin)
642  {
643  foreach (var refList in mixin.ClassReferences.MethodsReferences)
644  refList.Value.RemoveWhere(x => x == expression);
645 
646  foreach (var refList in mainModuleMixin.ClassReferences.MethodsReferences)
647  refList.Value.RemoveWhere(x => x == expression);
648  }
649 
650  /// <summary>
651  /// Find the mixin in which the parameter is a dependency
652  /// </summary>
653  /// <param name="mixin">the mixin</param>
654  /// <returns>the mixin that depends on the parameter</returns>
655  private ModuleMixin GetTopMixin(ModuleMixin mixin)
656  {
657  var topMixin = mainModuleMixin == mixin || mainModuleMixin.InheritanceList.Any(x => x == mixin) ? mainModuleMixin : null;
658  if (topMixin == null)
659  {
660  foreach (var externMixes in CompositionsPerVariable)
661  {
662  foreach (var externMix in externMixes.Value)
663  {
664  topMixin = externMix == mixin || externMix.InheritanceList.Any(x => x == mixin) ? externMix : null;
665  if (topMixin != null)
666  break;
667  }
668  if (topMixin != null)
669  break;
670  }
671  }
672  return topMixin;
673  }
674 
675  /// <summary>
676  /// Find a static method
677  /// </summary>
678  /// <param name="expression">the calling expression</param>
679  /// <returns>the correct called method</returns>
680  private MethodDeclaration FindStaticMethod(MethodInvocationExpression expression)
681  {
682  var defMixin = (expression.Target.TypeInference.Declaration as MethodDeclaration).GetTag(ParadoxTags.ShaderScope) as ModuleMixin;
683  defMixin = mixContext[defMixin.MixinName];
684  return defMixin.GetMethodFromExpression(expression.Target);
685  }
686 
687  /// <summary>
688  /// Gets the base stage method
689  /// </summary>
690  /// <param name="methodCall">the reference expression</param>
691  /// <returns>the base declaration</returns>
692  private MethodDeclaration GetBaseStageMethod(MethodInvocationExpression methodCall)
693  {
694  var mixin = methodCall.GetTag(ParadoxTags.CurrentShader) as ModuleMixin;
695  var vtReference = mixin.VirtualTable.GetBaseDeclaration(methodCall.Target.TypeInference.Declaration as MethodDeclaration);
696  foreach (var stageMethodList in StageMethodInheritance)
697  {
698  if (stageMethodList == null || stageMethodList.Count == 0)
699  continue;
700 
701  var firstOccurence = stageMethodList.First();
702  var occurenceMixin = firstOccurence.GetTag(ParadoxTags.ShaderScope) as ModuleMixin;
703  var listVTReference = occurenceMixin.VirtualTable.GetBaseDeclaration(firstOccurence);
704 
705  if (vtReference.Slot != listVTReference.Slot || vtReference.Shader != listVTReference.Shader)
706  continue;
707 
708  //TODO: can we call a base without overriding ?
709  for (int j = stageMethodList.Count - 1; j > 0; --j)
710  {
711  var decl = stageMethodList[j];
712  if (decl.GetTag(ParadoxTags.ShaderScope) as ModuleMixin == mixin)
713  return stageMethodList[j - 1];
714  }
715  //for (int j = stageMethodList.Count - 1; j >= 0; --j)
716  //{
717  // var decl = stageMethodList[j];
718  // if (decl.GetTag(ParadoxTags.ShaderScope) as ModuleMixin == mixin)
719  // {
720  // if (j == 0)
721  // return stageMethodList[0];
722  // return stageMethodList[j - 1];
723  // }
724  //}
725  }
726  return null;
727  }
728 
729  /// <summary>
730  /// Gets the last override of the method
731  /// </summary>
732  /// <param name="methodCall">the method call</param>
733  /// <returns>the declaration</returns>
734  private MethodDeclaration GetThisStageMethod(MethodInvocationExpression methodCall)
735  {
736  var mixin = methodCall.GetTag(ParadoxTags.CurrentShader) as ModuleMixin;
737  var vtReference = mixin.VirtualTable.GetBaseDeclaration(methodCall.Target.TypeInference.Declaration as MethodDeclaration);
738  foreach (var stageMethodList in StageMethodInheritance)
739  {
740  if (stageMethodList == null || stageMethodList.Count == 0)
741  continue;
742 
743  var firstOccurence = stageMethodList.First();
744  var occurenceMixin = firstOccurence.GetTag(ParadoxTags.ShaderScope) as ModuleMixin;
745  var listVTReference = occurenceMixin.VirtualTable.GetBaseDeclaration(firstOccurence);
746 
747  if (vtReference.Slot != listVTReference.Slot || vtReference.Shader != listVTReference.Shader)
748  continue;
749 
750  return stageMethodList.Last();
751  }
752  return null;
753  }
754 
755  /// <summary>
756  /// Solves both base and direct method calls
757  /// </summary>
758  /// <param name="mixin"></param>
759  private void PatchAllMethodInferences(ModuleMixin mixin)
760  {
761  mixin.InheritanceList.ForEach(PatchAllMethodInferences);
762  CompositionsPerVariable.Where(x => mixin.LocalVirtualTable.Variables.Any(y => y.Variable == x.Key)).ToList().ForEach(externMixes => externMixes.Value.ForEach(PatchAllMethodInferences));
763 
764  var topMixin = GetTopMixin(mixin);
765 
766  foreach (var baseCall in mixin.ParsingInfo.BaseMethodCalls)
767  {
768  MethodDeclaration decl = null;
769  if ((baseCall.Target.TypeInference.Declaration as MethodDeclaration).Qualifiers.Contains(ParadoxStorageQualifier.Stage))
770  decl = GetBaseStageMethod(baseCall);
771  else
772  decl = topMixin.GetBaseMethodFromExpression(baseCall.Target, mixin);
773 
774  if (decl != null)
775  {
776  RemoveFromMethodsReferences(baseCall, topMixin);
777 
778  baseCall.TypeInference.TargetType = decl.ReturnType;
779  baseCall.Target.TypeInference.Declaration = decl;
780 
781  AddToMethodsReferences(baseCall);
782  }
783  else
784  log.Error(ParadoxMessageCode.ErrorImpossibleBaseCall, baseCall.Span, baseCall, mixin.MixinName);
785  }
786 
787  // resolve this calls
788  foreach (var thisCall in mixin.ParsingInfo.ThisMethodCalls)
789  {
790  MethodDeclaration decl = null;
791  if ((thisCall.Target.TypeInference.Declaration as MethodDeclaration).Qualifiers.Contains(ParadoxStorageQualifier.Stage))
792  decl = GetThisStageMethod(thisCall);
793  else if (thisCall.ContainsTag(ParadoxTags.StaticRef))
794  decl = FindStaticMethod(thisCall);
795  else
796  decl = topMixin.GetMethodFromExpression(thisCall.Target);
797 
798  if (decl != null)
799  {
800  RemoveFromMethodsReferences(thisCall, topMixin);
801 
802  thisCall.TypeInference.TargetType = decl.ReturnType;
803  thisCall.Target.TypeInference.Declaration = decl;
804 
805  if (!thisCall.ContainsTag(ParadoxTags.StaticRef))
806  AddToMethodsReferences(thisCall);
807  }
808  else
809  log.Error(ParadoxMessageCode.ErrorImpossibleVirtualCall, thisCall.Span, thisCall, mixin.MixinName, mainModuleMixin.MixinName);
810  }
811  }
812 
813  /// <summary>
814  /// Rebranch the type inference for the stage variable reference in the extern
815  /// </summary>
816  /// <param name="externMix"></param>
817  private void InferStageVariables(ModuleMixin externMix)
818  {
819  var stageDict = externMix.ClassReferences.VariablesReferences.Where(x => x.Key.Qualifiers.Contains(ParadoxStorageQualifier.Stage)).ToDictionary(x => x.Key, x => x.Value);
820  foreach (var variable in stageDict)
821  {
822  var shaderName = (variable.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName;
823  var foundDeclaration = mainModuleMixin.ClassReferences.VariablesReferences.FirstOrDefault(x => x.Key.Name.Text == variable.Key.Name.Text && (x.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName == shaderName).Key;
824  if (foundDeclaration == null)// get by semantics if necessary
825  {
826  var semantic = variable.Key.Qualifiers.Values.OfType<Semantic>().FirstOrDefault();
827  if (semantic != null)
828  {
829  foundDeclaration = mainModuleMixin.ClassReferences.VariablesReferences.FirstOrDefault(
830  x =>
831  {
832  var varSemantic = x.Key.Qualifiers.Values.OfType<Semantic>().FirstOrDefault();
833  if (varSemantic != null && semantic.Name.Text == varSemantic.Name.Text)
834  return true;
835  return false;
836  }).Key;
837  }
838  }
839 
840  if (foundDeclaration != null)
841  {
842  mainModuleMixin.ClassReferences.VariablesReferences[foundDeclaration].UnionWith(variable.Value);
843  foreach (var varRef in variable.Value)
844  {
845  varRef.Expression.TypeInference.Declaration = foundDeclaration;
846  varRef.Expression.TypeInference.TargetType = foundDeclaration.Type;
847  }
848  }
849  else
850  {
851  log.Error(ParadoxMessageCode.ErrorMissingStageVariable, variable.Key.Span, variable, externMix.MixinName);
852  return;
853  }
854  }
855 
856  foreach (var key in stageDict.Keys)
857  externMix.ClassReferences.VariablesReferences.Remove(key);
858  }
859 
860  /// <summary>
861  /// Inference for extern calls
862  /// </summary>
863  /// <param name="mixin"></param>
864  private void ProcessExternReferences(ModuleMixin mixin)
865  {
866  mixin.InheritanceList.ForEach(ProcessExternReferences);
867  CompositionsPerVariable.Where(x => mixin.LocalVirtualTable.Variables.Any(y => y.Variable == x.Key)).ToList().ForEach(externMixes => externMixes.Value.ForEach(ProcessExternReferences));
868 
869  foreach (var externReferences in mixin.ExternReferences.VariablesReferences)
870  {
871  foreach (var expression in externReferences.Value)
872  {
873  var searchMixin = mixin;
874  var foundDefinition = FindVariable(expression.Expression, ref searchMixin);
875  if (foundDefinition != null) // should be always true
876  {
877  if (foundDefinition.Qualifiers.Contains(ParadoxStorageQualifier.Stage))
878  {
879 
880  var sameVar =
881  mixin.ClassReferences.VariablesReferences.FirstOrDefault(
882  x => x.Key.Name.Text == foundDefinition.Name.Text && (x.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName == (foundDefinition.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName).Key;
883  if (sameVar == null)
884  {
885  mixin.ClassReferences.VariablesReferences.Add(foundDefinition, new HashSet<ExpressionNodeCouple>());
886  sameVar = foundDefinition;
887  }
888  mixin.ClassReferences.VariablesReferences[sameVar].Add(expression);
889  expression.Expression.TypeInference.Declaration = sameVar;
890  expression.Expression.TypeInference.TargetType = sameVar.Type.ResolveType();
891  }
892  else
893  {
894  if (!mixin.ClassReferences.VariablesReferences.ContainsKey(foundDefinition))
895  mixin.ClassReferences.VariablesReferences.Add(foundDefinition, new HashSet<ExpressionNodeCouple>());
896  mixin.ClassReferences.VariablesReferences[foundDefinition].Add(expression);
897  expression.Expression.TypeInference.Declaration = foundDefinition;
898  expression.Expression.TypeInference.TargetType = foundDefinition.Type.ResolveType();
899  }
900  }
901  else
902  log.Error(ParadoxMessageCode.ErrorExternReferenceNotFound, expression.Expression.Span, expression, mixin.MixinName);
903  }
904  }
905  mixin.ExternReferences.VariablesReferences.Clear();
906 
907  foreach (var externReferences in mixin.ExternReferences.MethodsReferences)
908  {
909  foreach (var methodInvoc in externReferences.Value)
910  {
911  var searchMixin = mixin;
912  var foundDefinition = FindMethod(methodInvoc.Target, ref searchMixin);
913  if (foundDefinition != null) // should be always true
914  {
915  if (!mixin.ClassReferences.MethodsReferences.ContainsKey(foundDefinition))
916  mixin.ClassReferences.MethodsReferences.Add(foundDefinition, new HashSet<MethodInvocationExpression>());
917  mixin.ClassReferences.MethodsReferences[foundDefinition].Add(methodInvoc);
918  methodInvoc.Target.TypeInference.Declaration = foundDefinition;
919  }
920  else
921  log.Error(ParadoxMessageCode.ErrorExternReferenceNotFound, methodInvoc.Span, methodInvoc, mixin.MixinName);
922  }
923  }
924  mixin.ExternReferences.MethodsReferences.Clear();
925  }
926 
927  /// <summary>
928  /// Redo type inference for stage init variables
929  /// </summary>
930  /// <param name="moduleMixin">the module mixin to analyze</param>
931  private void ProcessStageInitReferences(ModuleMixin moduleMixin)
932  {
933  foreach (var variable in moduleMixin.StageInitReferences.VariablesReferences)
934  {
935  var varMixinName = ((ModuleMixin)variable.Key.GetTag(ParadoxTags.ShaderScope)).MixinName;
936  var mixin = MixinInheritance.FirstOrDefault(x => x.MixinName == varMixinName);
937  if (mixin == null)
938  {
939  log.Error(ParadoxMessageCode.ErrorStageMixinNotFound, new SourceSpan(), varMixinName, moduleMixin.MixinName);
940  return;
941  }
942 
943  var trueVar = mixin.ClassReferences.VariablesReferences.FirstOrDefault(x => x.Key.Name.Text == variable.Key.Name.Text).Key;
944  if (trueVar == null)
945  {
946  var sourceShader = ((ModuleMixin)variable.Key.GetTag(ParadoxTags.ShaderScope)).MixinName;
947  log.Error(ParadoxMessageCode.ErrorStageMixinVariableNotFound, new SourceSpan(), varMixinName, sourceShader, moduleMixin.MixinName);
948  return;
949  }
950 
951  foreach (var varRef in variable.Value)
952  {
953  varRef.Expression.TypeInference.Declaration = trueVar;
954  varRef.Expression.TypeInference.TargetType = trueVar.Type.ResolveType();
955  }
956 
957  mainModuleMixin.ClassReferences.VariablesReferences[trueVar].UnionWith(variable.Value);
958  }
959  foreach (var method in moduleMixin.StageInitReferences.MethodsReferences)
960  {
961  var varMixinName = ((ModuleMixin)method.Key.GetTag(ParadoxTags.ShaderScope)).MixinName;
962  var mixin = MixinInheritance.FirstOrDefault(x => x.MixinName == varMixinName);
963  if (mixin == null)
964  {
965  log.Error(ParadoxMessageCode.ErrorStageMixinNotFound, new SourceSpan(), varMixinName, moduleMixin.MixinName);
966  return;
967  }
968 
969  var trueVar = GetTopMixin(mixin).GetMethodFromDeclaration(method.Key);
970  if (trueVar == null)
971  {
972  log.Error(ParadoxMessageCode.ErrorStageMixinMethodNotFound, new SourceSpan(), varMixinName, method, moduleMixin.MixinName);
973  return;
974  }
975 
976  foreach (var varRef in method.Value)
977  {
978  varRef.Target.TypeInference.Declaration = trueVar;
979  varRef.Target.SetTag(ParadoxTags.VirtualTableReference, trueVar.GetTag(ParadoxTags.VirtualTableReference));
980  }
981 
982  mainModuleMixin.ClassReferences.MethodsReferences[trueVar].UnionWith(method.Value);
983  }
984  }
985 
986  /// <summary>
987  /// Relink stage references, extern references, merge static references from externs
988  /// </summary>
989  private void ProcessExterns()
990  {
991  ProcessExternReferences(mainModuleMixin);
992 
993  AddStageVariables(mainModuleMixin);
994  foreach (var externMix in CompositionsPerVariable.SelectMany(externMixes => externMixes.Value))
995  InferStageVariables(externMix);
996 
997  ProcessStageInitReferences(mainModuleMixin);
998  CompositionsPerVariable.SelectMany(externMixes => externMixes.Value).ToList().ForEach(ProcessStageInitReferences);
999 
1000  foreach (var externMix in CompositionsPerVariable.SelectMany(externMixes => externMixes.Value))
1001  {
1002  foreach (var variable in externMix.StaticReferences.VariablesReferences)
1003  {
1004  var varMixinName = (variable.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName;
1005  var staticVars = mainModuleMixin.StaticReferences.VariablesReferences.Where(x => (x.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName == varMixinName && x.Key.Name.Text == variable.Key.Name.Text).ToDictionary(x => x.Key, x => x.Value);
1006 
1007  // if the entry already exists, append to it
1008  if (staticVars.Count > 0)
1009  {
1010  var staticVar = staticVars.FirstOrDefault();
1011  foreach (var varRef in variable.Value)
1012  {
1013  varRef.Expression.TypeInference.Declaration = staticVar.Key;
1014  varRef.Expression.TypeInference.TargetType = staticVar.Key.Type.ResolveType();
1015  }
1016  staticVar.Value.UnionWith(variable.Value);
1017  }
1018  else // create the entry
1019  mainModuleMixin.StaticReferences.VariablesReferences.Add(variable.Key, variable.Value);
1020  }
1021  foreach (var method in externMix.StaticReferences.MethodsReferences)
1022  {
1023  var methodMixinName = (method.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName;
1024  var staticMethods = mainModuleMixin.StaticReferences.MethodsReferences.Where(x => (x.Key.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName == methodMixinName && x.Key.IsSameSignature(method.Key)).ToDictionary(x => x.Key, x => x.Value);
1025 
1026  // if the entry already exists, append to it
1027  if (staticMethods.Count > 0)
1028  {
1029  var staticMethod = staticMethods.FirstOrDefault();
1030  foreach (var methodRef in method.Value)
1031  methodRef.Target.TypeInference.Declaration = staticMethod.Key;
1032 
1033  staticMethod.Value.UnionWith(method.Value);
1034  }
1035  else // create the entry
1036  mainModuleMixin.StaticReferences.MethodsReferences.Add(method.Key, method.Value);
1037  }
1038  }
1039  }
1040 
1041  /// <summary>
1042  /// Rename all the variables
1043  /// </summary>
1044  private void RenameAllVariables()
1045  {
1046  int id = 0;
1047  RenameAllVariables(mainModuleMixin.ClassReferences, ref id);
1048  }
1049 
1050  /// <summary>
1051  /// Rename all the variables and their references based on the id
1052  /// </summary>
1053  /// <param name="references">the pool to rename</param>
1054  /// <param name="id">the id used to build the new name</param>
1055  private void RenameAllVariables(ReferencesPool references, ref int id)
1056  {
1057  foreach (var variable in references.VariablesReferences)
1058  {
1059  if (variable.Key.Name.Text == FlipRendertargetVariableName) // DO NOT RENAME THIS SPECIFIC VARIABLE
1060  continue;
1061 
1062  foreach (var varRef in variable.Value)
1063  {
1064  if (varRef.Expression is MemberReferenceExpression)
1065  {
1066  if (variable.Key.Qualifiers.Contains(ParadoxStorageQualifier.Stream)) // TODO: change test
1067  {
1068  (varRef.Expression as MemberReferenceExpression).Member = variable.Key.Name;
1069 
1070  var type = (varRef.Expression as MemberReferenceExpression).Target.TypeInference.TargetType;
1071  if (!(type == ParadoxType.Input || type == ParadoxType.Input2 || type == ParadoxType.Output))
1072  (varRef.Expression as MemberReferenceExpression).Target = new VariableReferenceExpression(new Identifier("streams"));
1073  }
1074  else if (variable.Key.Qualifiers.Contains(ParadoxStorageQualifier.PatchStream))
1075  {
1076  (varRef.Expression as MemberReferenceExpression).Member = variable.Key.Name;
1077  }
1078  else
1079  {
1080  var vre = new VariableReferenceExpression(variable.Key.Name);
1081  vre.TypeInference.Declaration = variable.Key;
1082  vre.TypeInference.TargetType = variable.Key.Type.ResolveType();
1083  ReplaceMemberReferenceExpressionByVariableReferenceExpression(varRef.Expression as MemberReferenceExpression, vre, varRef.Node);
1084  varRef.Expression = vre;
1085  }
1086  }
1087  else
1088  (varRef.Expression as VariableReferenceExpression).Name = variable.Key.Name;
1089  }
1090 
1091  variable.Key.Name.Text += "_id" + id;
1092  ++id;
1093  }
1094  }
1095 
1096  /// <summary>
1097  /// Rename the methods and their references
1098  /// </summary>
1099  /// <param name="references">the pool to rename</param>
1100  /// <param name="id">the id used to build the new name</param>
1101  private void RenameAllMethods(ReferencesPool references, HashSet<MethodDefinition> renameFreeMethods, ref int id)
1102  {
1103  foreach (var method in references.MethodsReferences)
1104  {
1105  if (renameFreeMethods.Contains(method.Key) || !(method.Key is MethodDefinition))
1106  continue;
1107 
1108  foreach (var methodRef in method.Value)
1109  {
1110  var targetMre = methodRef.Target as MemberReferenceExpression;
1111  if (targetMre != null)
1112  {
1113  var vre = new VariableReferenceExpression();
1114  methodRef.Target = vre;
1115  vre.TypeInference.Declaration = targetMre.TypeInference.Declaration;
1116  vre.TypeInference.TargetType = targetMre.TypeInference.TargetType;
1117  }
1118 
1119  var targetVre = methodRef.Target as VariableReferenceExpression;
1120  targetVre.Name = method.Key.Name;
1121  }
1122 
1123  method.Key.Name.Text += "_id" + id;
1124  ++id;
1125  }
1126  references.RegenKeys();
1127  }
1128 
1129  /// <summary>
1130  /// Rename all the methods
1131  /// </summary>
1132  private void RenameAllMethods()
1133  {
1134  // Find entry points
1135  var vertexShaderMethod = FindEntryPoint("VSMain");
1136  var hullShaderMethod = FindEntryPoint("HSMain");
1137  var hullConstantShaderMethod = FindEntryPoint("HSConstantMain");
1138  var domainShaderMethod = FindEntryPoint("DSMain");
1139  var geometryShaderMethod = FindEntryPoint("GSMain");
1140  var pixelShaderMethod = FindEntryPoint("PSMain");
1141  var computeShaderMethod = FindEntryPoint("CSMain");
1142 
1143  if (pixelShaderMethod != null && pixelShaderMethod.Body.Count == 0)
1144  pixelShaderMethod = null;
1145 
1146  var renameFreeMethods = new HashSet<MethodDefinition>();
1147 
1148  // store these methods to prevent their renaming
1149  if (vertexShaderMethod != null)
1150  renameFreeMethods.Add(vertexShaderMethod);
1151  if (hullShaderMethod != null)
1152  renameFreeMethods.Add(hullShaderMethod);
1153  if (hullConstantShaderMethod != null)
1154  renameFreeMethods.Add(hullConstantShaderMethod);
1155  if (domainShaderMethod != null)
1156  renameFreeMethods.Add(domainShaderMethod);
1157  if (geometryShaderMethod != null)
1158  renameFreeMethods.Add(geometryShaderMethod);
1159  if (pixelShaderMethod != null)
1160  renameFreeMethods.Add(pixelShaderMethod);
1161  if (computeShaderMethod != null)
1162  renameFreeMethods.Add(computeShaderMethod);
1163 
1164  int id = 0;
1165 
1166  RenameAllMethods(mainModuleMixin.ClassReferences, renameFreeMethods, ref id);
1167  }
1168 
1169  /// <summary>
1170  /// Finds all the function with the name
1171  /// </summary>
1172  /// <param name="name">the name of the function</param>
1173  /// <returns>a collection of all the functions with that name, correctly ordered</returns>
1174  private MethodDefinition FindEntryPoint(string name)
1175  {
1176  for (int i = MixinInheritance.Count - 1; i >= 0; --i)
1177  {
1178  var mixin = MixinInheritance[i];
1179  var count = 0;
1180  for (int j = 0; j < i; ++j)
1181  {
1182  count += mixin.MixinName == MixinInheritance[j].MixinName ? 1 : 0;
1183  }
1184 
1185  var method = mixin.LocalVirtualTable.Methods.FirstOrDefault(x => x.Method.Name.Text == name && x.Method is MethodDefinition);
1186  if (method != null && (count == 0 || method.Method.Qualifiers.Contains(ParadoxStorageQualifier.Clone)))
1187  return method.Method as MethodDefinition;
1188  }
1189  return null;
1190  }
1191 
1192  /// <summary>
1193  /// Creates a new AST with all the definitions
1194  /// </summary>
1195  private void GenerateShader()
1196  {
1197  MixedShader = new ShaderClassType(mainModuleMixin.MixinName);
1198 
1199  // Add structures, typedefs
1200  foreach (var mixin in MixinInheritance.Where(x => x.OccurenceId == 1))
1201  MixedShader.Members.AddRange(mixin.ParsingInfo.Typedefs);
1202  foreach (var mixin in MixinInheritance.Where(x => x.OccurenceId == 1))
1203  MixedShader.Members.AddRange(mixin.ParsingInfo.StructureDefinitions);
1204 
1205  // Create constant buffer
1206  GroupByConstantBuffer();
1207 
1208  // add the methods
1209  MixedShader.Members.AddRange(mainModuleMixin.ClassReferences.MethodsReferences.Select(x => x.Key).Where(x => x is MethodDefinition));
1210 
1211  // remove duplicates
1212  MixedShader.Members = MixedShader.Members.Distinct().ToList();
1213 
1214  // Create streams
1215  ParadoxStreamCreator.Run(MixedShader, mainModuleMixin, MixinInheritance, log);
1216 
1217  if (log.HasErrors)
1218  return;
1219 
1220  // deal with foreach statements
1221  ExpandForEachStatements(mainModuleMixin);
1222  foreach (var externMix in CompositionsPerVariable.SelectMany(x => x.Value))
1223  ExpandForEachStatements(externMix);
1224 
1225  // remove useless variables
1226  RemoveUselessVariables();
1227  }
1228 
1229  /// <summary>
1230  /// Remove useless variables
1231  /// </summary>
1232  private void RemoveUselessVariables()
1233  {
1234  var variablesUsages = MixedShader.Members.OfType<Variable>().ToDictionary(variable => variable, variable => false);
1235 
1236  foreach (var constantBuffer in MixedShader.Members.OfType<ConstantBuffer>())
1237  {
1238  foreach (var variable in constantBuffer.Members.OfType<Variable>().ToList())
1239  variablesUsages.Add(variable, false);
1240  }
1241 
1242  MixedShader.Members.RemoveAll(x => x is Variable && (x as Variable).Qualifiers.Contains(ParadoxStorageQualifier.Extern));
1243 
1244  var variableUsageVisitor = new ParadoxVariableUsageVisitor(variablesUsages);
1245  variableUsageVisitor.Run(MixedShader);
1246 
1247  foreach (var variable in MixedShader.Members.OfType<Variable>().ToList())
1248  {
1249  bool used;
1250  if (variablesUsages.TryGetValue(variable, out used))
1251  {
1252  if (!used && variable.Name.Text != FlipRendertargetVariableName)
1253  MixedShader.Members.Remove(variable);
1254  }
1255  }
1256 
1257  foreach (var constantBuffer in MixedShader.Members.OfType<ConstantBuffer>())
1258  {
1259  foreach (var variable in constantBuffer.Members.OfType<Variable>().ToList())
1260  {
1261  bool used;
1262  if (variablesUsages.TryGetValue(variable, out used))
1263  {
1264  if (!used && variable.Name.Text != FlipRendertargetVariableName)
1265  constantBuffer.Members.Remove(variable);
1266  }
1267  }
1268  }
1269  }
1270 
1271  /// <summary>
1272  /// Test if the variable should be in a constant buffer
1273  /// </summary>
1274  /// <param name="variable">the variable</param>
1275  /// <returns>true/false</returns>
1276  private bool IsOutOfCBufferVariable(Variable variable)
1277  {
1278  return variable.Type is SamplerType || variable.Type is SamplerStateType || variable.Type is TextureType || variable.Type is StateType || variable.Type.ResolveType() is ObjectType || variable.Qualifiers.Contains(StorageQualifier.Const);
1279  }
1280 
1281  /// <summary>
1282  /// Test if the variable should be in a constant buffer
1283  /// </summary>
1284  /// <param name="variable">the variable</param>
1285  /// <returns>true/false</returns>
1286  private bool KeepVariableInCBuffer(Variable variable)
1287  {
1288  return !(variable.Qualifiers.Contains(ParadoxStorageQualifier.Extern) || variable.Qualifiers.Contains(ParadoxStorageQualifier.Stream) || variable.Qualifiers.Contains(ParadoxStorageQualifier.PatchStream) || IsOutOfCBufferVariable(variable));
1289  }
1290 
1291  // Group everything by constant buffers
1292  private void GroupByConstantBuffer()
1293  {
1294  MergeSameSemanticVariables(mainModuleMixin.ClassReferences.VariablesReferences.Select(x => x.Key).ToList());
1295  MergeReferenceVariables(mainModuleMixin.ClassReferences.VariablesReferences.Select(x => x.Key).ToList());
1296  var usefulVars = mainModuleMixin.ClassReferences.VariablesReferences.Select(x => x.Key).Where(KeepVariableInCBuffer);
1297  var varList = usefulVars.Where(x => x.ContainsTag(ParadoxTags.ConstantBuffer)).ToList();
1298  var groupedVarList = varList.GroupBy(x => x.GetTag(ParadoxTags.ConstantBuffer) as string).Select(x => x.ToList()).ToList();
1299 
1300  foreach (var group in groupedVarList)
1301  {
1302  var cbufferName = group.FirstOrDefault().GetTag(ParadoxTags.ConstantBuffer) as string;
1303  var cbuffer = new ConstantBuffer { Type = SiliconStudio.Shaders.Ast.Hlsl.ConstantBufferType.Constant, Name = cbufferName };
1304  cbuffer.Members.AddRange(group);
1305 
1306  MixedShader.Members.Add(cbuffer);
1307  }
1308 
1309  var remainingVars = usefulVars.Where(x => !x.ContainsTag(ParadoxTags.ConstantBuffer)).ToList();
1310  var globalBuffer = new ConstantBuffer { Type = SiliconStudio.Shaders.Ast.Hlsl.ConstantBufferType.Constant, Name = "Globals" };
1311  if (remainingVars.Count > 0)
1312  {
1313  globalBuffer.Members.AddRange(remainingVars);
1314  MixedShader.Members.Add(globalBuffer);
1315  }
1316 
1317  MixedShader.Members.AddRange(mainModuleMixin.ClassReferences.VariablesReferences.Select(x => x.Key).Where(IsOutOfCBufferVariable));
1318  }
1319 
1320 
1321  /// <summary>
1322  /// Merge all the variables with the same semantic and rename them (but typeinference is not correct)
1323  /// </summary>
1324  private void MergeSameSemanticVariables(List<Variable> variables)
1325  {
1326  var duplicateVariables = new List<Variable>(); // list of variables that will be removed
1327  var allVariablesWithSemantic = variables.Where(x => x.Qualifiers.Values.Any(y => y is Semantic)).ToList();
1328  foreach (var variable in allVariablesWithSemantic)
1329  {
1330  if (!duplicateVariables.Contains(variable))
1331  {
1332  var semantic = variable.Qualifiers.OfType<Semantic>().First();
1333 
1334  var sameSemanticVariables = allVariablesWithSemantic.Where(x => x != variable && x.Qualifiers.Values.OfType<Semantic>().Any(y => AreSameSemantics(y.Name.Text, semantic.Name.Text))).ToList();
1335 
1336  foreach (var sameSemVar in sameSemanticVariables)
1337  {
1338  var cbufferName = variable.ContainsTag(ParadoxTags.ConstantBuffer) ? variable.GetTag(ParadoxTags.ConstantBuffer) as string : null;
1339  var newcbufferName = sameSemVar.ContainsTag(ParadoxTags.ConstantBuffer) ? sameSemVar.GetTag(ParadoxTags.ConstantBuffer) as string : null;
1340  if (cbufferName != null ^ newcbufferName != null)
1341  variable.SetTag(ParadoxTags.ConstantBuffer, cbufferName ?? newcbufferName);
1342  else if (cbufferName != null && cbufferName != newcbufferName)
1343  {
1344  var sourceMixinName = (variable.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName;
1345  var newMixinName = (sameSemVar.GetTag(ParadoxTags.ShaderScope) as ModuleMixin).MixinName;
1346  log.Error(ParadoxMessageCode.ErrorSemanticCbufferConflict, variable.Span, variable, sourceMixinName, sameSemVar, newMixinName, semantic, cbufferName, newcbufferName);
1347  }
1348 
1349  foreach (var exp in mainModuleMixin.ClassReferences.VariablesReferences[sameSemVar])
1350  {
1351  if (exp.Expression is VariableReferenceExpression)
1352  (exp.Expression as VariableReferenceExpression).Name = variable.Name;
1353  else if (exp.Expression is MemberReferenceExpression)
1354  (exp.Expression as MemberReferenceExpression).Member = variable.Name;
1355 
1356  exp.Expression.TypeInference.Declaration = variable;
1357  }
1358  mainModuleMixin.ClassReferences.VariablesReferences[variable].UnionWith(mainModuleMixin.ClassReferences.VariablesReferences[sameSemVar]);
1359  sameSemVar.Name = variable.Name;
1360  }
1361 
1362  duplicateVariables.AddRange(sameSemanticVariables);
1363  }
1364  }
1365 
1366  mainModuleMixin.ClassReferences.RegenKeys();
1367  duplicateVariables.ForEach(variable => mainModuleMixin.ClassReferences.VariablesReferences.Remove(variable));
1368  }
1369 
1370  /// <summary>
1371  /// Merge variables that are references of another one
1372  /// </summary>
1373  /// <param name="variables"></param>
1374  private void MergeReferenceVariables(List<Variable> variables)
1375  {
1376  var duplicateVariables = new List<Variable>();
1377  foreach (var variable in variables.Where(x => x.InitialValue is MemberReferenceExpression || x.InitialValue is VariableReferenceExpression))
1378  {
1379  //find reference
1380  var target = variable.InitialValue.TypeInference.Declaration as Variable;
1381  if (target != null)
1382  {
1383  foreach (var exp in mainModuleMixin.ClassReferences.VariablesReferences[variable])
1384  {
1385  if (exp.Expression is VariableReferenceExpression)
1386  (exp.Expression as VariableReferenceExpression).Name = target.Name;
1387  else if (exp.Expression is MemberReferenceExpression)
1388  (exp.Expression as MemberReferenceExpression).Member = target.Name;
1389 
1390  exp.Expression.TypeInference.Declaration = target;
1391  }
1392  mainModuleMixin.ClassReferences.VariablesReferences[target].UnionWith(mainModuleMixin.ClassReferences.VariablesReferences[variable]);
1393  variable.Name = target.Name;
1394  duplicateVariables.Add(variable);
1395  }
1396  }
1397 
1398  mainModuleMixin.ClassReferences.RegenKeys();
1399  duplicateVariables.ForEach(variable => mainModuleMixin.ClassReferences.VariablesReferences.Remove(variable));
1400  }
1401 
1402  #endregion
1403 
1404  #region Static helpers
1405 
1406  /// <summary>
1407  /// Replaces the ForEachStatements in the mixin by ForStatements
1408  /// </summary>
1409  /// <param name="mixin">the mixin</param>
1410  private static void ExpandForEachStatements(ModuleMixin mixin)
1411  {
1412  foreach (var statementNodeCouple in mixin.ParsingInfo.ForEachStatements.Where(x => !(x.Statement as ForEachStatement).Variable.Qualifiers.Contains(ParadoxStorageQualifier.Extern)))
1413  {
1414  var newStatement = ExpandForEachStatement(statementNodeCouple.Statement as ForEachStatement);
1415  if (newStatement != null)
1416  {
1417  var replace = new ParadoxReplaceVisitor(statementNodeCouple.Statement, newStatement);
1418  replace.Run(statementNodeCouple.Node);
1419  }
1420  }
1421 
1422  mixin.InheritanceList.ForEach(ExpandForEachStatements);
1423  }
1424 
1425  /// <summary>
1426  /// Creates a ForStatement with the same behavior
1427  /// </summary>
1428  /// <param name="forEachStatement">the ForEachStatement</param>
1429  /// <returns>the ForStatement</returns>
1430  private static ForStatement ExpandForEachStatement(ForEachStatement forEachStatement)
1431  {
1432  if (forEachStatement != null)
1433  {
1434  var collec = forEachStatement.Collection.TypeInference.Declaration as Variable;
1435  LiteralExpression dimLit = null;
1436  if (collec.Type is ArrayType)
1437  {
1438  if ((collec.Type as ArrayType).Dimensions.Count == 1)
1439  {
1440  dimLit = (collec.Type as ArrayType).Dimensions[0] as LiteralExpression;
1441  }
1442  }
1443 
1444  if (dimLit != null)
1445  {
1446  var initializer = new Variable(ScalarType.Int, forEachStatement.Variable.Name.Text + "Iter", new LiteralExpression(0));
1447  var vre = new VariableReferenceExpression(initializer.Name);
1448  var condition = new BinaryExpression(BinaryOperator.Less, vre, dimLit);
1449  var next = new UnaryExpression(UnaryOperator.PreIncrement, vre);
1450  ForStatement forStatement = new ForStatement(new DeclarationStatement(initializer), condition, next);
1451  var body = new BlockStatement();
1452 
1453  var variable = forEachStatement.Variable;
1454  variable.InitialValue = new IndexerExpression(forEachStatement.Collection, new VariableReferenceExpression(initializer));
1455  body.Statements.Add(new DeclarationStatement(variable));
1456 
1457  if (forEachStatement.Body is BlockStatement)
1458  body.Statements.AddRange((forEachStatement.Body as BlockStatement).Statements);
1459  else
1460  body.Statements.Add(forEachStatement.Body);
1461 
1462  forStatement.Body = body;
1463 
1464  return forStatement;
1465  }
1466 
1467  // TODO: multidimension-array?
1468  // TODO: unroll?
1469  // TODO: multiple foreach?
1470  }
1471  return null;
1472  }
1473 
1474  /// <summary>
1475  /// Replace a MemberReferenceExpression by a VariableReferenceExpression in the AST
1476  /// </summary>
1477  /// <param name="memberReferenceExpression">the member reference expression.</param>
1478  /// <param name="variableReferenceExpression">the variable reference expression.</param>
1479  /// <param name="parentNode">the parent node.</param>
1480  private static void ReplaceMemberReferenceExpressionByVariableReferenceExpression(MemberReferenceExpression memberReferenceExpression, VariableReferenceExpression variableReferenceExpression, Node parentNode)
1481  {
1482  var replacor = new ParadoxReplaceVisitor(memberReferenceExpression, variableReferenceExpression);
1483  replacor.Run(parentNode);
1484  }
1485 
1486  /// <summary>
1487  /// Compare the semantics
1488  /// </summary>
1489  /// <param name="sem0"></param>
1490  /// <param name="sem1"></param>
1491  /// <returns></returns>
1492  private static bool AreSameSemantics(string sem0, string sem1)
1493  {
1494  var upperSem0 = sem0.ToUpperInvariant();
1495  var upperSem1 = sem1.ToUpperInvariant();
1496 
1497  if (upperSem0 == upperSem1)
1498  return true;
1499 
1500  var i = upperSem0.Length - 1;
1501  while (i > 0 && char.IsDigit(upperSem0[i]))
1502  --i;
1503  string trimSem0 = upperSem0.Substring(0, i + 1);
1504  int sem0Index = i == upperSem0.Length - 1 ? 0 : Int32.Parse(upperSem0.Substring(i + 1, upperSem0.Length - i - 1));
1505 
1506  i = upperSem1.Length - 1;
1507  while (i > 0 && char.IsDigit(upperSem1[i]))
1508  --i;
1509  string trimSem1 = upperSem1.Substring(0, i + 1);
1510  int sem1Index = i == upperSem1.Length - 1 ? 0 : Int32.Parse(upperSem1.Substring(i + 1, upperSem1.Length - i - 1));
1511 
1512  return trimSem0 == trimSem1 && sem0Index == sem1Index;
1513  }
1514 
1515  #endregion
1516  }
1517 }
SiliconStudio.Shaders.Ast.Hlsl.ConstantBuffer ConstantBuffer
TypeBase TargetType
Gets or sets the type.
Identifier Name
Gets or sets the name.
Definition: Variable.cs:77
static readonly SiliconStudio.Shaders.Ast.StorageQualifier Abstract
Override keyword (override).
Describes a binary expression.
static readonly ScalarType Int
Scalar int.
Definition: ScalarType.cs:37
UnaryOperator
Unary operator used in all binary expressions (except assignment expression).
TypeInference TypeInference
Gets or sets the resolved reference.
Definition: TypeBase.cs:69
static readonly SiliconStudio.Shaders.Ast.StorageQualifier Stream
Stream keyword (stream).
string Text
Gets or sets the name.
Definition: Identifier.cs:77
static readonly SiliconStudio.Shaders.Ast.StorageQualifier PatchStream
Patch stream keyword (patchstream).
Qualifier Qualifiers
Gets or sets the qualifiers.
Definition: Variable.cs:53
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
Expression Collection
Gets or sets the condition.
BinaryOperator
Binary operator used in all binary expressions (except assignment expression).
A class to collect parsing/expression messages.
Definition: LoggerResult.cs:13
static readonly StorageQualifier Const
Const qualifier.
A method definition with a body of statements.
Abstract node.
Definition: Node.cs:15
_In_ size_t count
Definition: DirectXTexP.h:174
bool HasErrors
Gets or sets a value indicating whether this instance has errors.
Definition: LoggerResult.cs:29
A variable declaration.
Definition: Variable.cs:11
object Value
Gets or sets the value.
Definition: Literal.cs:57
Variable Variable
Gets or sets the initializer.
A member reference in the form {this}.{Name}
SiliconStudio.Shaders.Ast.StorageQualifier StorageQualifier
static readonly SiliconStudio.Shaders.Ast.StorageQualifier Stage
Stage keyword (stage).
SiliconStudio.Shaders.Ast.SourceSpan SourceSpan
Definition: Node.cs:8
Provides a dictionary of cloned values, where the [key] is the original object and [value] the new ob...
Declaration of a constant buffer.
Toplevel container of a shader parsing result.
Definition: Shader.cs:12
bool Contains(CompositeEnum enumValue)
Determines whether [contains] [the specified enum value].
static readonly Ast.StorageQualifier Extern
Extern modifier.
static readonly SiliconStudio.Shaders.Ast.StorageQualifier Clone
Clone keyword (clone).
A field of a struct.
Definition: Literal.cs:13