Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ParadoxStreamCreator.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.Ast;
8 using SiliconStudio.Paradox.Shaders.Parser.Utility;
9 using SiliconStudio.Shaders.Ast;
10 using SiliconStudio.Shaders.Ast.Hlsl;
11 using SiliconStudio.Shaders.Utility;
12 
14 
15 namespace SiliconStudio.Paradox.Shaders.Parser.Mixins
16 {
17  internal class ParadoxStreamCreator
18  {
19  #region private static members
20 
21  private readonly static string[] GeometryShaderUnOptimizedSemantics = { "SV_RenderTargetArrayIndex" };
22 
23  #endregion
24 
25  #region Private members
26 
27  /// <summary>
28  /// The shader
29  /// </summary>
30  private ShaderClassType shader;
31 
32  /// <summary>
33  /// the main ModuleMixin corresonding to the shader
34  /// </summary>
35  private ModuleMixin mainModuleMixin;
36 
37  /// <summary>
38  /// Ordered list of all the mixin in their appearance order
39  /// </summary>
40  private List<ModuleMixin> mixinInheritance = new List<ModuleMixin>();
41 
42  /// <summary>
43  /// the entry points of the shader
44  /// </summary>
45  private HashSet<MethodDefinition> entryPointMethods = new HashSet<MethodDefinition>();
46 
47  /// <summary>
48  /// All the streams usages
49  /// </summary>
50  private Dictionary<MethodDeclaration, List<StreamUsageInfo>> streamsUsages = new Dictionary<MethodDeclaration, List<StreamUsageInfo>>();
51 
52  /// <summary>
53  /// List of methods that need streams structure.
54  /// </summary>
55  private Dictionary<PdxShaderStage, List<MethodDeclaration>> methodsPerShaderStage = new Dictionary<PdxShaderStage, List<MethodDeclaration>>();
56 
57  /// <summary>
58  /// Stream analyzer
59  /// </summary>
60  private ParadoxStreamAnalyzer streamAnalyzer;
61 
62  /// <summary>
63  /// the error logger
64  /// </summary>
65  private LoggerResult errorWarningLog;
66 
67  #endregion
68 
69  #region Constructor
70 
71  private ParadoxStreamCreator(ShaderClassType shaderClassType, ModuleMixin mixin, List<ModuleMixin> mixins, LoggerResult errorLog)
72  {
73  shader = shaderClassType;
74  mainModuleMixin = mixin;
75  mixinInheritance = mixins;
76  errorWarningLog = errorLog ?? new LoggerResult();
77  }
78 
79  public static void Run(ShaderClassType shaderClassType, ModuleMixin mixin, List<ModuleMixin> mixins, LoggerResult errorLog)
80  {
81  var streamCreator = new ParadoxStreamCreator(shaderClassType, mixin, mixins, errorLog);
82  streamCreator.Run();
83  }
84 
85  #endregion
86 
87  #region Public method
88 
89  public void Run()
90  {
91  streamAnalyzer = new ParadoxStreamAnalyzer(errorWarningLog);
92  streamAnalyzer.Run(shader);
93 
94  if (errorWarningLog.HasErrors)
95  return;
96 
97  streamsUsages = streamAnalyzer.StreamsUsageByMethodDefinition;
98 
99  // Find entry points
100  var vertexShaderMethod = FindEntryPoint("VSMain");
101  var hullShaderMethod = FindEntryPoint("HSMain");
102  var hullConstantShaderMethod = FindEntryPoint("HSConstantMain");
103  var domainShaderMethod = FindEntryPoint("DSMain");
104  var geometryShaderMethod = FindEntryPoint("GSMain");
105  var pixelShaderMethod = FindEntryPoint("PSMain");
106  var computeShaderMethod = FindEntryPoint("CSMain");
107 
108  if (vertexShaderMethod != null)
109  vertexShaderMethod.Attributes.Add(new AttributeDeclaration { Name = new Identifier("EntryPoint"), Parameters = new List<Literal> { new Literal("Vertex") } });
110  if (pixelShaderMethod != null)
111  pixelShaderMethod.Attributes.Add(new AttributeDeclaration { Name = new Identifier("EntryPoint"), Parameters = new List<Literal> { new Literal("Pixel") } });
112  if (geometryShaderMethod != null)
113  geometryShaderMethod.Attributes.Add(new AttributeDeclaration { Name = new Identifier("EntryPoint"), Parameters = new List<Literal> { new Literal("Geometry") } });
114  if (hullShaderMethod != null)
115  hullShaderMethod.Attributes.Add(new AttributeDeclaration { Name = new Identifier("EntryPoint"), Parameters = new List<Literal> { new Literal("Hull") } });
116  if (domainShaderMethod != null)
117  domainShaderMethod.Attributes.Add(new AttributeDeclaration { Name = new Identifier("EntryPoint"), Parameters = new List<Literal> { new Literal("Domain") } });
118  if (computeShaderMethod != null)
119  computeShaderMethod.Attributes.Add(new AttributeDeclaration { Name = new Identifier("EntryPoint"), Parameters = new List<Literal> { new Literal("Compute") } });
120 
121  if (!(hullShaderMethod == null && hullConstantShaderMethod == null && domainShaderMethod == null) && (hullShaderMethod == null || hullConstantShaderMethod == null || domainShaderMethod == null))
122  {
123  errorWarningLog.Error(ParadoxMessageCode.ErrorIncompleteTesselationShader, new SourceSpan());
124  return;
125  }
126 
127  StreamStageUsage streamStageUsageVS = vertexShaderMethod == null ? null : StreamAnalysisPerShader(vertexShaderMethod.GetTag(ParadoxTags.ShaderScope) as ModuleMixin, vertexShaderMethod, PdxShaderStage.Vertex);
128  StreamStageUsage streamStageUsageHS = hullShaderMethod == null ? null : StreamAnalysisPerShader(hullShaderMethod.GetTag(ParadoxTags.ShaderScope) as ModuleMixin, hullShaderMethod, PdxShaderStage.Hull);
129  StreamStageUsage streamStageUsageHSCS = hullConstantShaderMethod == null ? null : StreamAnalysisPerShader(hullConstantShaderMethod.GetTag(ParadoxTags.ShaderScope) as ModuleMixin, hullConstantShaderMethod, PdxShaderStage.Constant);
130  StreamStageUsage streamStageUsageDS = domainShaderMethod == null ? null : StreamAnalysisPerShader(domainShaderMethod.GetTag(ParadoxTags.ShaderScope) as ModuleMixin, domainShaderMethod, PdxShaderStage.Domain);
131  StreamStageUsage streamStageUsageGS = geometryShaderMethod == null ? null : StreamAnalysisPerShader(geometryShaderMethod.GetTag(ParadoxTags.ShaderScope) as ModuleMixin, geometryShaderMethod, PdxShaderStage.Geometry);
132  StreamStageUsage streamStageUsagePS = pixelShaderMethod == null ? null : StreamAnalysisPerShader(pixelShaderMethod.GetTag(ParadoxTags.ShaderScope) as ModuleMixin, pixelShaderMethod, PdxShaderStage.Pixel);
133  StreamStageUsage streamStageUsageCS = computeShaderMethod == null ? null : StreamAnalysisPerShader(computeShaderMethod.GetTag(ParadoxTags.ShaderScope) as ModuleMixin, computeShaderMethod, PdxShaderStage.Compute);
134 
135  // pathc some usage so that variables are correctly passed even if they are not explicitely used.
136  if (streamStageUsageGS != null && streamStageUsageVS != null)
137  {
138  var needToAdd = true;
139  foreach (var variable in streamStageUsageGS.OutStreamList.OfType<Variable>())
140  {
141  var sem = variable.Qualifiers.OfType<Semantic>().FirstOrDefault();
142  if (sem != null && sem.Name.Text == "SV_Position")
143  {
144  needToAdd = false;
145  break;
146  }
147  }
148 
149  if (needToAdd)
150  {
151  // get the ShadingPosition variable
152  foreach (var variable in streamStageUsageVS.OutStreamList.OfType<Variable>())
153  {
154  var sem = variable.Qualifiers.OfType<Semantic>().FirstOrDefault();
155  if (sem != null && sem.Name.Text == "SV_Position")
156  {
157  streamStageUsageGS.OutStreamList.Add(variable);
158  break;
159  }
160 
161  }
162  }
163  // TODO: it may have more variables like this one.
164  }
165 
166 
167  var shaderStreamsUsage = new List<StreamStageUsage>();
168 
169  // store these methods to prevent their renaming
170  if (vertexShaderMethod != null)
171  {
172  entryPointMethods.Add(vertexShaderMethod);
173  shaderStreamsUsage.Add(streamStageUsageVS);
174  }
175  if (hullShaderMethod != null)
176  {
177  entryPointMethods.Add(hullShaderMethod);
178  shaderStreamsUsage.Add(streamStageUsageHS);
179  }
180  if (hullConstantShaderMethod != null)
181  {
182  entryPointMethods.Add(hullConstantShaderMethod);
183  shaderStreamsUsage.Add(streamStageUsageHSCS);
184  }
185  if (domainShaderMethod != null)
186  {
187  entryPointMethods.Add(domainShaderMethod);
188  shaderStreamsUsage.Add(streamStageUsageDS);
189  }
190  if (geometryShaderMethod != null)
191  {
192  entryPointMethods.Add(geometryShaderMethod);
193  shaderStreamsUsage.Add(streamStageUsageGS);
194  }
195  if (pixelShaderMethod != null)
196  {
197  entryPointMethods.Add(pixelShaderMethod);
198  shaderStreamsUsage.Add(streamStageUsagePS);
199  }
200  if (computeShaderMethod != null)
201  {
202  entryPointMethods.Add(computeShaderMethod);
203  }
204 
205  BubbleUpStreamUsages(shaderStreamsUsage);
206 
207  if (computeShaderMethod != null)
208  ComputeShaderStreamAnalysis(streamStageUsageCS);
209 
210  StructType outputStructure = null;
211 
212  // remove the now useless tags and typeinferences to accelerate cloning
213  var tagCleaner = new ParadoxTagCleaner();
214  tagCleaner.Run(shader);
215 
216  outputStructure = GenerateStreams(vertexShaderMethod, streamStageUsageVS, "VS", outputStructure);
217  outputStructure = GenerateStreamsForHullShader(hullShaderMethod, hullConstantShaderMethod, streamStageUsageHS, "HS", outputStructure);
218  outputStructure = GenerateStreamsForDomainShader(domainShaderMethod, streamStageUsageDS, "DS", outputStructure);
219  outputStructure = GenerateStreamsWithSpecialDataInput(geometryShaderMethod, streamStageUsageGS, "GS", outputStructure);
220  outputStructure = GenerateStreams(pixelShaderMethod, streamStageUsagePS, "PS", outputStructure, false);
221 
222  outputStructure = GenerateStreams(computeShaderMethod, streamStageUsageCS, "CS", null);
223 
224  RemoveUselessAndSortMethods();
225  }
226 
227  #endregion
228 
229  #region Private methods
230 
231  /// <summary>
232  /// Sort the methods based on their calls
233  /// </summary>
234  private void RemoveUselessAndSortMethods()
235  {
236  var methods = shader.Members.OfType<MethodDeclaration>().ToList();
237  shader.Members.RemoveAll(methods.Contains);
238 
239  var zeroCalledMethods = new HashSet<MethodDeclaration>();
240  var methodReferenceCounter = methods.ToDictionary(x => x, x => 0);
241 
242  foreach (var reference in streamsUsages)
243  {
244  foreach (var usage in reference.Value.Where(x => x.CallType == StreamCallType.Method))
245  {
246  int value;
247  if (methodReferenceCounter.TryGetValue(usage.MethodDeclaration, out value))
248  methodReferenceCounter[usage.MethodDeclaration] = value + 1;
249  else if (!entryPointMethods.Contains(usage.MethodDeclaration))
250  zeroCalledMethods.Add(usage.MethodDeclaration);
251  }
252  }
253 
254 
255  zeroCalledMethods.UnionWith(methodReferenceCounter.Where(x => x.Value == 0 && !entryPointMethods.Contains(x.Key)).Select(x => x.Key));
256  BuildOrderedMethodUsageList(zeroCalledMethods, methodReferenceCounter);
257 
258  var finalMethodsList = BuildOrderedMethodUsageList(new HashSet<MethodDeclaration>(entryPointMethods), methodReferenceCounter);
259  finalMethodsList.Reverse();
260  shader.Members.AddRange(finalMethodsList);
261  }
262 
263  /// <summary>
264  /// Recursively create a list of all the methods that are exclusively used from the start ones
265  /// </summary>
266  /// <param name="startList">the list of starting methods</param>
267  /// <param name="methodReferenceCounter">all the methods</param>
268  /// <returns></returns>
269  private List<MethodDeclaration> BuildOrderedMethodUsageList(HashSet<MethodDeclaration> startList, Dictionary<MethodDeclaration, int> methodReferenceCounter)
270  {
271  var finalMethodsList = new List<MethodDeclaration>();
272  while (startList.Count > 0)
273  {
274  var newZeroCalledMethods = new List<MethodDeclaration>();
275 
276  foreach (var method in startList)
277  {
278  finalMethodsList.Add(method);
279 
280  List<StreamUsageInfo> reference;
281  if (streamsUsages.TryGetValue(method, out reference))
282  {
283  foreach (var usage in reference.Where(x => x.CallType == StreamCallType.Method))
284  {
285  int value;
286  if (methodReferenceCounter.TryGetValue(usage.MethodDeclaration, out value))
287  {
288  methodReferenceCounter[usage.MethodDeclaration] = value - 1;
289  if (value == 1)
290  newZeroCalledMethods.Add(usage.MethodDeclaration);
291  }
292  }
293  }
294  }
295 
296  startList.Clear();
297  startList.UnionWith(newZeroCalledMethods);
298  }
299 
300  return finalMethodsList;
301  }
302 
303  /// <summary>
304  /// Finds all the function with the name
305  /// </summary>
306  /// <param name="name">the name of the function</param>
307  /// <returns>a collection of all the functions with that name, correctly ordered</returns>
308  private MethodDefinition FindEntryPoint(string name)
309  {
310  for (int i = mixinInheritance.Count - 1; i >= 0; --i)
311  {
312  var mixin = mixinInheritance[i];
313  var count = 0;
314  for (int j = 0; j < i; ++j)
315  {
316  count += mixin.MixinName == mixinInheritance[j].MixinName ? 1 : 0;
317  }
318 
319  var method = mixin.LocalVirtualTable.Methods.FirstOrDefault(x => x.Method.Name.Text == name && x.Method is MethodDefinition);
320  if (method != null && (count == 0 || method.Method.Qualifiers.Contains(ParadoxStorageQualifier.Clone)))
321  return method.Method as MethodDefinition;
322  }
323  return null;
324  }
325 
326  /// <summary>
327  /// Get the streams usage for this entrypoint
328  /// </summary>
329  /// <param name="moduleMixin">the current module mixin</param>
330  /// <param name="entryPoint">the entrypoint method</param>
331  /// <returns>a StreamStageUsage containing the streams usages</returns>
332  private StreamStageUsage StreamAnalysisPerShader(ModuleMixin moduleMixin, MethodDeclaration entryPoint, PdxShaderStage shaderStage)
333  {
334  var visitedMethods = new List<MethodDeclaration>();
335  var streamStageUsage = new StreamStageUsage { ShaderStage = shaderStage };
336  FindStreamsUsage(entryPoint, streamStageUsage.InStreamList, streamStageUsage.OutStreamList, visitedMethods);
337  visitedMethods.Clear();
338 
339  return streamStageUsage;
340  }
341 
342  /// <summary>
343  /// Finds the usage of the streams
344  /// </summary>
345  /// <param name="currentMethod">the current method</param>
346  /// <param name="inStreamList">list of in-streams</param>
347  /// <param name="outStreamList">list of out-streams</param>
348  /// <param name="visitedMethods">list of already visited methods</param>
349  private void FindStreamsUsage(MethodDeclaration currentMethod, List<IDeclaration> inStreamList, List<IDeclaration> outStreamList, List<MethodDeclaration> visitedMethods)
350  {
351  if (visitedMethods.Contains(currentMethod))
352  {
353  errorWarningLog.Error(ParadoxMessageCode.ErrorRecursiveCall, currentMethod.Span, currentMethod);
354  return;
355  }
356 
357  if (currentMethod != null)
358  {
359  var newListVisitedMethods = new List<MethodDeclaration>();
360  newListVisitedMethods.AddRange(visitedMethods);
361  newListVisitedMethods.Add(currentMethod);
362 
363  List<StreamUsageInfo> streamUsageList;
364  if (streamsUsages.TryGetValue(currentMethod, out streamUsageList))
365  {
366  // look for stream usage inside the function
367  foreach (var streamUsage in streamUsageList)
368  {
369  if (streamUsage.CallType == StreamCallType.Member)
370  {
371  var isOutStream = outStreamList.Contains(streamUsage.Variable);
372  var isInStream = inStreamList.Contains(streamUsage.Variable);
373 
374  if (streamUsage.Usage == StreamUsage.Write && !isOutStream)
375  outStreamList.Add(streamUsage.Variable);
376  else if (streamUsage.Usage == StreamUsage.Read && !isOutStream && !isInStream) // first read
377  inStreamList.Add(streamUsage.Variable);
378  }
379  else if (streamUsage.CallType == StreamCallType.Method)
380  {
381  if (streamUsage.MethodDeclaration != null) // way to check the built-in functions (could be improved?)
382  FindStreamsUsage(streamUsage.MethodDeclaration, inStreamList, outStreamList, newListVisitedMethods);
383  }
384  else if (streamUsage.CallType != StreamCallType.Direct) // should not happen
385  errorWarningLog.Error(ParadoxMessageCode.ErrorStreamUsageInitialization, streamUsage.Expression.Span, streamUsage.Expression);
386  }
387  }
388  }
389  }
390 
391  /// <summary>
392  /// Pass the stream usage accros the stages.
393  /// </summary>
394  /// <param name="streamStageUsages">the ordered stream usage list</param>
395  private void BubbleUpStreamUsages(List<StreamStageUsage> streamStageUsages)
396  {
397  for (int i = streamStageUsages.Count - 1; i > 0; --i)
398  {
399  var nextStreamUsage = streamStageUsages[i];
400  var prevStreamUsage = streamStageUsages[i - 1];
401 
402  // pixel output is only SV_Targetx and SV_Depth, everything else intermediate variable
403  if (nextStreamUsage.ShaderStage == PdxShaderStage.Pixel)
404  {
405  var semVar = new List<IDeclaration>();
406  var nonSemVar = new List<IDeclaration>();
407  foreach (var variable in nextStreamUsage.OutStreamList)
408  {
409  var sem = (variable as Variable).Qualifiers.OfType<Semantic>().FirstOrDefault();
410  if (sem != null && (sem.Name.Text.StartsWith("SV_Target") || sem.Name.Text == "SV_Depth"))
411  semVar.Add(variable);
412  else
413  nonSemVar.Add(variable);
414  }
415  nextStreamUsage.InterStreamList.AddRange(nonSemVar);
416  nextStreamUsage.OutStreamList = semVar;
417  }
418 
419  // NOTE: from this point, nextStreamUsage.OutStreamList is correct.
420 
421  // add necessary variables to output and input of previous stage
422  foreach (var variable in nextStreamUsage.InStreamList)
423  {
424  if (!prevStreamUsage.OutStreamList.Contains(variable))
425  {
426  prevStreamUsage.OutStreamList.Add(variable);
427 
428  if (!prevStreamUsage.InStreamList.Contains(variable))
429  prevStreamUsage.InStreamList.Add(variable);
430  }
431  }
432 
433  // keep variable from prev output only if they are necessary to next stage OR their semantics force them to be in it
434  var toKeep = new List<IDeclaration>();
435  foreach (var variable in prevStreamUsage.OutStreamList)
436  {
437  var sem = (variable as Variable).Qualifiers.OfType<Semantic>().FirstOrDefault();
438  if (nextStreamUsage.InStreamList.Contains(variable)
439  || ((nextStreamUsage.ShaderStage == PdxShaderStage.Pixel || nextStreamUsage.ShaderStage == PdxShaderStage.Geometry) && sem != null && sem.Name.Text == "SV_Position"))
440  toKeep.Add(variable);
441  else if (nextStreamUsage.ShaderStage == PdxShaderStage.Pixel && prevStreamUsage.ShaderStage == PdxShaderStage.Geometry && sem != null && sem.Name.Text == "SV_RenderTargetArrayIndex")
442  {
443  toKeep.Add(variable);
444  nextStreamUsage.InStreamList.Add(variable);
445  }
446  else
447  prevStreamUsage.InterStreamList.Add(variable);
448  }
449 
450  prevStreamUsage.OutStreamList.Clear();
451  prevStreamUsage.OutStreamList.AddRange(toKeep);
452  }
453  }
454 
455  /// <summary>
456  /// Organize the streams for the compute shader
457  /// </summary>
458  /// <param name="streamStageUsage">the StreamStageUsage of the compute stage</param>
459  private void ComputeShaderStreamAnalysis(StreamStageUsage streamStageUsage)
460  {
461  if (streamStageUsage.ShaderStage == PdxShaderStage.Compute)
462  {
463  streamStageUsage.InterStreamList.AddRange(streamStageUsage.OutStreamList);
464  streamStageUsage.OutStreamList.Clear();
465  }
466  }
467 
468  /// <summary>
469  /// Generates a stream structure and add them to the Ast
470  /// </summary>
471  /// <param name="entryPoint">the entrypoint function</param>
472  /// <param name="streamStageUsage">the stream usage in this stage</param>
473  /// <param name="stageName">the name of the stage</param>
474  /// <param name="prevOuputStructure">the output structutre from the previous stage</param>
475  /// <returns>the new output structure</returns>
476  private StructType GenerateStreams(MethodDefinition entryPoint, StreamStageUsage streamStageUsage, string stageName, StructType prevOuputStructure, bool autoGenSem = true)
477  {
478  if (entryPoint != null)
479  {
480  // create the stream structures
481  var inStreamStruct = prevOuputStructure ?? CreateStreamStructure(streamStageUsage.InStreamList, stageName + "_INPUT");
482  var outStreamStruct = CreateStreamStructure(streamStageUsage.OutStreamList, stageName + "_OUTPUT", true, autoGenSem);
483 
484  var intermediateStreamStruct = CreateIntermediateStructType(streamStageUsage, stageName);
485 
486  // modify the entrypoint
487  if (inStreamStruct.Fields.Count != 0)
488  {
489  entryPoint.Parameters.Add(new Parameter(new TypeName(inStreamStruct.Name), "input"));
490  entryPoint.Parameters[0].Qualifiers.Values.Remove(ParameterQualifier.InOut);
491  }
492 
493  // add the declaration statements to the entrypoint and fill with the values
494  entryPoint.Body.InsertRange(0, CreateStreamFromInput(intermediateStreamStruct, "streams", inStreamStruct, new VariableReferenceExpression("input")));
495  if (outStreamStruct.Fields.Count != 0)
496  {
497  entryPoint.Body.AddRange(CreateOutputFromStream(outStreamStruct, "output", intermediateStreamStruct, "streams"));
498  entryPoint.Body.Add(new ReturnStatement { Value = new VariableReferenceExpression("output") });
499  entryPoint.ReturnType = new TypeName(outStreamStruct.Name);
500  }
501 
502  // explore all the called functions
503  var visitedMethods = new HashSet<MethodDeclaration>();
504  var methodsWithStreams = new List<MethodDeclaration>();
505  PropagateStreamsParameter(entryPoint, inStreamStruct, intermediateStreamStruct, outStreamStruct, visitedMethods, methodsWithStreams);
506 
507  CheckCrossStageMethodCall(streamStageUsage.ShaderStage, methodsWithStreams);
508 
509  if (prevOuputStructure == null)
510  shader.Members.Insert(0, inStreamStruct);
511  if (outStreamStruct.Fields.Count != 0)
512  shader.Members.Insert(0, outStreamStruct);
513  shader.Members.Insert(0, intermediateStreamStruct);
514 
515  return outStreamStruct;
516  }
517 
518  return prevOuputStructure;
519  }
520 
521  /// <summary>
522  /// Generates a stream structure and add them to the Ast - for the geometry shader
523  /// </summary>
524  /// <param name="entryPoint">the entrypoint function</param>
525  /// <param name="streamStageUsage">the stream usage in this stage</param>
526  /// <param name="stageName">the name of the stage</param>
527  /// <param name="prevOuputStructure">the output structutre from the previous stage</param>
528  /// <returns>the new output structure</returns>
529  private StructType GenerateStreamsWithSpecialDataInput(MethodDefinition entryPoint, StreamStageUsage streamStageUsage, string stageName, StructType prevOuputStructure)
530  {
531  if (entryPoint != null)
532  {
533  var inStreamStruct = prevOuputStructure ?? CreateStreamStructure(streamStageUsage.InStreamList, stageName + "_INPUT");
534  var outStreamStruct = CreateStreamStructure(streamStageUsage.OutStreamList, stageName + "_OUTPUT");
535 
536  var mixin = entryPoint.GetTag(ParadoxTags.ShaderScope) as ModuleMixin;
537 
538  var intermediateStreamStruct = CreateIntermediateStructType(streamStageUsage, stageName);
539 
540  // put the streams declaration at the beginning of the method body
541  var streamsDeclaration = new DeclarationStatement(new Variable(new TypeName(intermediateStreamStruct.Name), "streams") { InitialValue = new CastExpression { From = new LiteralExpression(0), Target = new TypeName(intermediateStreamStruct.Name) } });
542  entryPoint.Body.Insert(0, streamsDeclaration);
543 
544  // add the declaration statements to the entrypoint and fill with the values
545  var outputStatements = CreateOutputFromStream(outStreamStruct, "output", intermediateStreamStruct, "streams").ToList();
546  var outputVre = new VariableReferenceExpression(((outputStatements.First() as DeclarationStatement).Content as Variable).Name);
547 
548  var replacor = new ParadoxReplaceAppend(streamAnalyzer.AppendMethodCalls, outputStatements, outputVre);
549  ReplaceAppendMethod(entryPoint, replacor);
550 
551  var visitedMethods = new Stack<MethodDeclaration>();
552  var inStructType = new TypeName(inStreamStruct.Name);
553  var outStructType = new TypeName(outStreamStruct.Name);
554  RecursiveRename(entryPoint, inStructType, null, outStructType, null, visitedMethods);
555 
556  // explore all the called functions
557  var streamsVisitedMethods = new HashSet<MethodDeclaration>();
558  var methodsWithStreams = new List<MethodDeclaration>();
559  PropagateStreamsParameter(entryPoint, inStreamStruct, intermediateStreamStruct, outStreamStruct, streamsVisitedMethods, methodsWithStreams);
560 
561  CheckCrossStageMethodCall(streamStageUsage.ShaderStage, methodsWithStreams);
562 
563  if (prevOuputStructure == null)
564  shader.Members.Insert(0, inStreamStruct);
565  shader.Members.Insert(0, outStreamStruct);
566  shader.Members.Insert(0, intermediateStreamStruct);
567 
568  return outStreamStruct;
569  }
570 
571  return prevOuputStructure;
572  }
573 
574  /// <summary>
575  /// Replace the append methods
576  /// </summary>
577  /// <param name="entryPoint">the entrypoint method</param>
578  /// <param name="replacor">the visitor</param>
579  private void ReplaceAppendMethod(MethodDefinition entryPoint, ParadoxReplaceAppend replacor)
580  {
581  replacor.Run(entryPoint);
582 
583  List<StreamUsageInfo> nextMethods;
584  if (streamsUsages.TryGetValue(entryPoint, out nextMethods))
585  nextMethods.Where(x => x.CallType == StreamCallType.Method).Select(x => x.MethodDeclaration as MethodDefinition).Where(x => x != null).ToList().ForEach(x => ReplaceAppendMethod(x, replacor));
586  }
587 
588 
589 
590  /// <summary>
591  /// Generates a stream structure and add them to the Ast - for the hull shader and hull shader constant
592  /// </summary>
593  /// <param name="entryPoint">the entrypoint function</param>
594  /// <param name="entryPointHSConstant">entrypoint for the hull shader constant</param>
595  /// <param name="streamStageUsage">the stream usage in this stage</param>
596  /// <param name="stageName">the name of the stage</param>
597  /// <param name="prevOuputStructure">the output structutre from the previous stage</param>
598  /// <returns>the new output structure</returns>
599  private StructType GenerateStreamsForHullShader(MethodDefinition entryPoint, MethodDefinition entryPointHSConstant, StreamStageUsage streamStageUsage, string stageName, StructType prevOuputStructure)
600  {
601  if (entryPoint != null)
602  {
603  // same behavior as geometry shader
604  var outStreamStruct = GenerateStreamsWithSpecialDataInput(entryPoint, streamStageUsage, stageName, prevOuputStructure);
605  var inStreamStruct = prevOuputStructure ?? shader.Members.OfType<StructType>().FirstOrDefault(x => x.Name.Text == stageName + "_INPUT");
606  var intermediateStreamStruct = shader.Members.OfType<StructType>().FirstOrDefault(x => x.Name.Text == stageName + "_STREAMS");
607 
608  if (inStreamStruct == null)
609  throw new Exception("inStreamStruct cannot be null");
610 
611  var inStructType = new TypeName(inStreamStruct.Name);
612  var outStructType = new TypeName(outStreamStruct.Name);
613 
614  // get the Output parameter, its name and remove it
615  var outputName = "output";
616  var outputParam = entryPoint.Parameters.FirstOrDefault(x => x.Type.Name.Text == outStreamStruct.Name.Text);
617  if (outputParam != null)
618  {
619  outputName = outputParam.Name.Text; // get the name of the parameter
620  entryPoint.Parameters.Remove(outputParam); // remove the parameter
621  }
622 
623  entryPoint.Body.Add(new ReturnStatement { Value = new VariableReferenceExpression(outputName) });
624  entryPoint.Body.Insert(0, CreateStructInit(outStreamStruct, outputName));
625  entryPoint.ReturnType = outStructType;
626 
627  if (entryPointHSConstant != null)
628  GenerateStreamsForHullShaderConstant(entryPointHSConstant, inStructType, outStructType);
629 
630  return outStreamStruct;
631  }
632 
633  return prevOuputStructure;
634  }
635 
636  /// <summary>
637  /// Modify the Hull shader constant
638  /// </summary>
639  /// <param name="entryPoint">the entrypoint method</param>
640  /// <param name="inStreamStructTypeName">the input structure of the Hull shader</param>
641  /// <param name="outStreamStructTypeName">the output structure of the Hull shader</param>
642  private void GenerateStreamsForHullShaderConstant(MethodDefinition entryPoint, TypeName inStreamStructTypeName, TypeName outStreamStructTypeName)
643  {
644  if (entryPoint != null)
645  {
646  var constStreamStruct = CreateStreamStructure(mainModuleMixin.VirtualTable.Variables.Select(x => x.Variable).Where(x => x.Qualifiers.Contains(ParadoxStorageQualifier.PatchStream)).Distinct().ToList<IDeclaration>(), "HS_CONSTANTS");
647  var typeConst = new TypeName(constStreamStruct.Name);
648 
649  var visitedMethods = new Stack<MethodDeclaration>();
650  RecursiveRename(entryPoint, inStreamStructTypeName, outStreamStructTypeName, outStreamStructTypeName, typeConst, visitedMethods);
651 
652  // get the Constants parameter, its name and remove it
653  var constParamName = "constants";
654  var constParam = entryPoint.Parameters.FirstOrDefault(x => x.Type.Name.Text == constStreamStruct.Name.Text);
655  if (constParam != null)
656  {
657  constParamName = constParam.Name.Text;
658  entryPoint.Parameters.Remove(constParam); // remove the parameter
659  }
660 
661  var constDecl = new DeclarationStatement(
662  new Variable(typeConst, constParamName)
663  {
664  InitialValue = new CastExpression { From = new LiteralExpression(0), Target = new TypeName(constStreamStruct.Name) }
665  });
666 
667  entryPoint.Body.Insert(0, constDecl); // insert structure instance declaration
668  entryPoint.Body.Add(new ReturnStatement(new VariableReferenceExpression(constParamName))); // add a return statement
669 
670  entryPoint.ReturnType = typeConst; // change the return type
671 
672  shader.Members.Insert(0, constStreamStruct);
673  }
674  }
675 
676  /// <summary>
677  /// Generates a stream structure and add them to the Ast - for the domain shader
678  /// </summary>
679  /// <param name="entryPoint">the entrypoint function</param>
680  /// <param name="streamStageUsage">the stream usage in this stage</param>
681  /// <param name="stageName">the name of the stage</param>
682  /// <param name="prevOuputStructure">the output structutre from the previous stage</param>
683  /// <returns>the new output structure</returns>
684  private StructType GenerateStreamsForDomainShader(MethodDefinition entryPoint, StreamStageUsage streamStageUsage, string stageName, StructType prevOuputStructure)
685  {
686  if (entryPoint != null)
687  {
688  var outStreamStruct = GenerateStreamsForHullShader(entryPoint, null, streamStageUsage, stageName, prevOuputStructure);
689 
690  var visitedMethods = new Stack<MethodDeclaration>();
691  RecursiveRename(entryPoint, null, null, null, new TypeName("HS_CONSTANTS"), visitedMethods);
692 
693  return outStreamStruct;
694  }
695 
696  return prevOuputStructure;
697  }
698 
699  /// <summary>
700  /// Checks if a function needs to have a stream strucutre added in its declaration
701  /// </summary>
702  /// <param name="methodDefinition">the method definition</param>
703  /// <param name="inputStream">The stage input structure stream.</param>
704  /// <param name="intermediateStream">the stream structure</param>
705  /// <param name="outputStream">The stage output stream structure.</param>
706  /// <param name="visitedMethods">the list of already visited methods</param>
707  /// <param name="methodsWithStreams">The list of methods that have a streams argument.</param>
708  /// <returns>true if needed, false otherwise</returns>
709  private bool PropagateStreamsParameter(MethodDefinition methodDefinition, StructType inputStream, StructType intermediateStream, StructType outputStream, HashSet<MethodDeclaration> visitedMethods, List<MethodDeclaration> methodsWithStreams)
710  {
711  var needStream = false;
712 
713  if (methodDefinition != null)
714  {
715  if (visitedMethods.Contains(methodDefinition))
716  return methodDefinition.Parameters.Count > 0 && methodDefinition.Parameters[0].Type == intermediateStream;
717 
718  List<StreamUsageInfo> streamUsageInfos;
719  if (streamsUsages.TryGetValue(methodDefinition, out streamUsageInfos))
720  {
721  needStream = streamUsageInfos.Any(x => x.CallType == StreamCallType.Member || x.CallType == StreamCallType.Direct);
722  visitedMethods.Add(methodDefinition);
723 
724  List<MethodDeclaration> calls;
725  if (TryGetMethodCalls(methodDefinition, out calls))
726  needStream = calls.Aggregate(needStream, (res, calledmethod) => res | PropagateStreamsParameter(calledmethod as MethodDefinition, inputStream, intermediateStream, outputStream, visitedMethods, methodsWithStreams));
727 
728  if (needStream && !entryPointMethods.Contains(methodDefinition))
729  {
730  var param = new Parameter(new TypeName(intermediateStream.Name), "streams");
731 
732  foreach (var methodRef in mainModuleMixin.ClassReferences.MethodsReferences[methodDefinition])
733  {
734  var vre = new VariableReferenceExpression(param.Name) { TypeInference = { Declaration = param, TargetType = param.Type } };
735  methodRef.Arguments.Insert(0, vre);
736  }
737 
738  param.Qualifiers |= ParameterQualifier.InOut;
739  methodDefinition.Parameters.Insert(0, param);
740 
741  methodsWithStreams.Add(methodDefinition);
742  }
743  }
744 
745  TransformStreamsAssignments(methodDefinition, inputStream, intermediateStream, outputStream);
746  }
747  return needStream;
748  }
749 
750 
751  /// <summary>
752  /// Transform stream assignments with correct input/ouput structures
753  /// </summary>
754  /// <param name="methodDefinition">the current method</param>
755  /// <param name="inputStreamStruct">the input structure of the stage</param>
756  /// <param name="intermediateStreamStruct">the intermediate structure of the stage</param>
757  /// <param name="outputStreamStruct">the output structure of the stage</param>
758  private void TransformStreamsAssignments(MethodDefinition methodDefinition, StructType inputStreamStruct, StructType intermediateStreamStruct, StructType outputStreamStruct)
759  {
760  // replace stream assignement with field values assignements
761  foreach (var assignment in streamAnalyzer.AssignationsToStream)
762  {
763  StatementList parent;
764  var index = SearchExpressionStatement(methodDefinition.Body, assignment, out parent);
765  if (index < 0 || parent == null)
766  continue;
767 
768  // TODO: check that it is "output = streams"
769  var statementList = CreateOutputFromStream(outputStreamStruct, (assignment.Target as VariableReferenceExpression).Name.Text, intermediateStreamStruct, "streams").ToList();
770  statementList.RemoveAt(0); // do not keep the variable declaration
771  methodDefinition.Body.RemoveAt(index);
772  methodDefinition.Body.InsertRange(index, statementList);
773  }
774 
775  // replace stream assignement with field values assignements
776  foreach (var assignment in streamAnalyzer.StreamAssignations)
777  {
778  StatementList parent;
779  var index = SearchExpressionStatement(methodDefinition.Body, assignment, out parent);
780  if (index < 0 || parent == null)
781  continue;
782 
783  var statementList = CreateStreamFromInput(intermediateStreamStruct, "streams", inputStreamStruct, assignment.Value, false).ToList();
784  statementList.RemoveAt(0); // do not keep the variable declaration
785  parent.RemoveAt(index);
786  parent.InsertRange(index, statementList);
787  }
788  }
789 
790  /// <summary>
791  /// Search a statement in method.
792  /// </summary>
793  /// <param name="topStatement">The statement to look into</param>
794  /// <param name="expression">The expression to look for.</param>
795  /// <param name="parentStatement">The statement list where the expression was found.</param>
796  /// <returns>The index of the statement in the statement list.</returns>
797  private int SearchExpressionStatement(Statement topStatement, Expression expression, out StatementList parentStatement)
798  {
799  // handle special case because BlockStatement.Children returns children of child (Statements members)
800  if (topStatement is BlockStatement)
801  topStatement = ((BlockStatement)topStatement).Statements;
802  if (topStatement is StatementList)
803  {
804  var statementList = (StatementList)topStatement;
805  var index = statementList.IndexOf(statementList.OfType<ExpressionStatement>().FirstOrDefault(x => x.Expression == expression));
806  if (index >= 0)
807  {
808  parentStatement = (StatementList)topStatement;
809  return index;
810  }
811 
812  }
813 
814  foreach (var statement in topStatement.Childrens().OfType<Statement>())
815  {
816  var index = SearchExpressionStatement(statement, expression, out parentStatement);
817  if (index >= 0)
818  return index;
819  }
820 
821  parentStatement = null;
822  return -1;
823  }
824 
825  /// <summary>
826  /// Recursively rename the input/output types
827  /// </summary>
828  /// <param name="methodDeclaration">the method to explore</param>
829  /// <param name="inputName">the TypeName for Input</param>
830  /// <param name="input2Name">the TypeName for Input2</param>
831  /// <param name="outputName">the TypeName for Output</param>
832  /// <param name="constantsName">the TypeName for Constants</param>
833  /// <param name="visitedMethods">the already visited methods</param>
834  private void RecursiveRename(MethodDeclaration methodDeclaration, TypeName inputName, TypeName input2Name, TypeName outputName, TypeName constantsName, Stack<MethodDeclaration> visitedMethods)
835  {
836  if (methodDeclaration == null || visitedMethods.Contains(methodDeclaration))
837  return;
838 
839  RenameInputOutput(methodDeclaration, inputName, input2Name, outputName, constantsName);
840  visitedMethods.Push(methodDeclaration);
841 
842  List<MethodDeclaration> calls;
843  if (TryGetMethodCalls(methodDeclaration, out calls))
844  {
845  foreach (var calledmethod in calls)
846  RecursiveRename(calledmethod, inputName, input2Name, outputName, constantsName, visitedMethods);
847  }
848  }
849 
850  /// <summary>
851  /// Get all the calls from the current method
852  /// </summary>
853  /// <param name="currentMethod">the current method</param>
854  /// <param name="calledMethods">list of method called</param>
855  /// <returns>true if calls were found</returns>
856  //private bool TryGetMethodCalls(MethodDeclaration currentMethod, out List<MethodInvocationExpression> calledMethods)
857  private bool TryGetMethodCalls(MethodDeclaration currentMethod, out List<MethodDeclaration> calledMethods)
858  {
859  List<StreamUsageInfo> streamUsageInfos;
860  if (streamsUsages.TryGetValue(currentMethod, out streamUsageInfos))
861  {
862  //calledMethods = streamUsageInfos.Where(x => x.CallType == StreamCallType.Method).Select(x => x.MethodReference).ToList();
863  calledMethods = streamUsageInfos.Where(x => x.CallType == StreamCallType.Method).Select(x => x.MethodDeclaration).ToList();
864  return true;
865  }
866 
867  calledMethods = null;
868  return false;
869  }
870 
871  /// <summary>
872  /// rename the input/ouput of a method
873  /// </summary>
874  /// <param name="methodDeclaration">the method</param>
875  /// <param name="inputName">the type replacement for Input</param>
876  /// <param name="input2Name">the type replacement for Input2</param>
877  /// <param name="outputName">the type replacement for Output</param>
878  /// <param name="constantsName">the type replacement for Constants</param>
879  private void RenameInputOutput(MethodDeclaration methodDeclaration, TypeName inputName, TypeName input2Name, TypeName outputName, TypeName constantsName)
880  {
881  if (inputName != null)
882  {
883  var replacor = new ParadoxReplaceVisitor(ParadoxType.Input, inputName);
884  replacor.Run(methodDeclaration);
885  }
886  if (input2Name != null)
887  {
888  var replacor = new ParadoxReplaceVisitor(ParadoxType.Input2, input2Name);
889  replacor.Run(methodDeclaration);
890  }
891  if (outputName != null)
892  {
893  var replacor = new ParadoxReplaceVisitor(ParadoxType.Output, outputName);
894  replacor.Run(methodDeclaration);
895  }
896  if (constantsName != null)
897  {
898  var replacor = new ParadoxReplaceVisitor(ParadoxType.Constants, constantsName);
899  replacor.Run(methodDeclaration);
900  }
901  }
902 
903  /// <summary>
904  /// Check that methods with streams are not called across several stages.
905  /// </summary>
906  /// <param name="shaderStage">The current shader stage to check.</param>
907  /// <param name="methodsWithStreams">The list of methods that need streams in that stage.</param>
908  private void CheckCrossStageMethodCall(PdxShaderStage shaderStage, List<MethodDeclaration> methodsWithStreams)
909  {
910  foreach (var stageList in methodsPerShaderStage)
911  {
912  var stage = stageList.Key;
913  if (stage != shaderStage) // should always be true
914  {
915  foreach (var method in methodsWithStreams)
916  {
917  if (stageList.Value.Contains(method))
918  {
919  errorWarningLog.Error(ParadoxMessageCode.ErrorCrossStageMethodCall, method.Span, method, stage, shaderStage);
920  }
921  }
922  }
923  }
924  methodsPerShaderStage.Add(shaderStage, methodsWithStreams);
925  }
926 
927  #endregion
928 
929  #region Private static methods
930 
931  /// <summary>
932  /// Creates assignement statements with its default value
933  /// </summary>
934  /// <param name="streamStruct">the stream structure</param>
935  /// <param name="streamName">the name of the stream</param>
936  /// <param name="inputStruct">the input structure</param>
937  /// <param name="initialValue">the initial value</param>
938  /// <param name="scopeStack">???</param>
939  /// <returns>A collection of statements</returns>
940  private static IEnumerable<Statement> AssignStreamFromInput(StructType streamStruct, string streamName, StructType inputStruct, Expression initialValue, bool basicTransformation)
941  {
942  foreach (var currentField in inputStruct.Fields)
943  {
944  // Ignore fields that don't exist in Streams.
945  // It could happen if HSConstantMain references a stream (gets added to HS_OUTPUT),
946  // and in HSMain CreateStreamFromInput() is called (this stream doesn't exist in DS_STREAMS).
947  if (streamStruct.Fields.All(x => x.Name != currentField.Name))
948  continue;
949 
950  // If we have a scope stack (advanced analysis), then convert expression by appending
951  // field to each reference to a variable of inputStruct type
952  // i.e. "output = input1 * 3 + input2 * 5" will become "output.A = input1.A * 3 + input2.A * 5"
953  // Otherwise consider it is as a simple a variable reference and directly append field.
954  if (basicTransformation)
955  {
956  yield return new ExpressionStatement(
958  AssignmentOperator.Default,
959  new MemberReferenceExpression(new VariableReferenceExpression(streamName), currentField.Name),
960  new MemberReferenceExpression(initialValue, currentField.Name)));
961  }
962  else
963  {
964  //yield return AssignStreamFieldFromInput(streamName, inputStruct, initialValue, scopeStack, currentField);
965  foreach (var field in streamStruct.Fields.Where(x => x.Name == currentField.Name)) // TODO: where might be useless
966  {
967  if (field.Type is ArrayType)
968  {
969  //create a for loop
970 
971  var iteratorName = field.Name.Text + "_Iter";
972  var iterator = new Variable(ScalarType.Int, iteratorName, new LiteralExpression(0));
973  var start = new DeclarationStatement(iterator);
974  var condition = new BinaryExpression(BinaryOperator.Less, new VariableReferenceExpression(iterator), (field.Type as ArrayType).Dimensions[0]);
975  var next = new UnaryExpression(UnaryOperator.PreIncrement, new VariableReferenceExpression(iterator));
976  var forLoop = new ForStatement(start, condition, next);
977 
978  var fieldAssigner = new StreamFieldVisitor(field, new VariableReferenceExpression(iterator));
979  var clonedExpression = fieldAssigner.Run(ParadoxAssignmentCloner.Run(initialValue));
980 
981  forLoop.Body = new ExpressionStatement(
983  AssignmentOperator.Default,
984  new IndexerExpression(new MemberReferenceExpression(new VariableReferenceExpression(streamName), currentField.Name), new VariableReferenceExpression(iterator)),
985  clonedExpression));
986 
987  yield return forLoop;
988  }
989  else
990  {
991  var fieldAssigner = new StreamFieldVisitor(field);
992  //var clonedExpression = fieldAssigner.Run(initialValue.DeepClone());
993  var clonedExpression = fieldAssigner.Run(ParadoxAssignmentCloner.Run(initialValue));
994 
995  yield return new ExpressionStatement(
997  AssignmentOperator.Default,
998  new MemberReferenceExpression(new VariableReferenceExpression(streamName), currentField.Name),
999  clonedExpression));
1000  }
1001  }
1002  }
1003  }
1004  }
1005 
1006  /// <summary>
1007  /// Creates assignement statements with its default value
1008  /// </summary>
1009  /// <param name="outputStruct">the output structure</param>
1010  /// <param name="outputName">the name of the output stream</param>
1011  /// <param name="streamStruct">the stream structure</param>
1012  /// <param name="streamName">the name of the stream</param>
1013  /// <returns>a collection of statements</returns>
1014  private static IEnumerable<Statement> AssignOutputFromStream(StructType outputStruct, string outputName, StructType streamStruct, string streamName)
1015  {
1016  foreach (var currentField in outputStruct.Fields)
1017  {
1018  yield return new ExpressionStatement(
1020  AssignmentOperator.Default,
1021  new MemberReferenceExpression(new VariableReferenceExpression(outputName), currentField.Name),
1022  new MemberReferenceExpression(new VariableReferenceExpression(streamName), currentField.Name)));
1023  }
1024  }
1025 
1026  /// <summary>
1027  /// Creates a stream structure and assign its default values
1028  /// </summary>
1029  /// <param name="streamStruct">the structure</param>
1030  /// <param name="streamName">the name of the stream</param>
1031  /// <param name="inputStruct">the inputStructure</param>
1032  /// <param name="initialValue">the initial value of the struture</param>
1033  /// <param name="scopeStack">???</param>
1034  /// <returns>a collection of statements to insert in the body of a method</returns>
1035  private static IEnumerable<Statement> CreateStreamFromInput(StructType streamStruct, string streamName, StructType inputStruct, Expression initialValue, bool basicTransformation = true)
1036  {
1037  yield return CreateStructInit(streamStruct, streamName);
1038 
1039  foreach (var statement in AssignStreamFromInput(streamStruct, streamName, inputStruct, initialValue, basicTransformation))
1040  {
1041  yield return statement;
1042  }
1043  }
1044 
1045  /// <summary>
1046  /// Creates an output stream structure and assign its default values
1047  /// </summary>
1048  /// <param name="outputStruct">the structuer</param>
1049  /// <param name="outputName">the name of the structure</param>
1050  /// <param name="streamStruct">>the initial value of the struture</param>
1051  /// <param name="streamName">the name of the stream</param>
1052  /// <returns>a collection of statements to insert in the body of a method</returns>
1053  private static IEnumerable<Statement> CreateOutputFromStream(StructType outputStruct, string outputName, StructType streamStruct, string streamName)
1054  {
1055  yield return CreateStructInit(outputStruct, outputName);
1056 
1057  foreach (var statement in AssignOutputFromStream(outputStruct, outputName, streamStruct, streamName))
1058  {
1059  yield return statement;
1060  }
1061  }
1062 
1063  /// <summary>
1064  /// Generate a stream structure
1065  /// </summary>
1066  /// <param name="streamsDeclarationList">the list of the declarations</param>
1067  /// <param name="structName">the name of the structure</param>
1068  /// <returns>the structure</returns>
1069  private static StructType CreateStreamStructure(List<IDeclaration> streamsDeclarationList, string structName, bool useSem = true, bool addAutoSem = true)
1070  {
1071  var tempStruct = new StructType { Name = new Identifier(structName) };
1072  foreach (var streamDecl in streamsDeclarationList)
1073  {
1074  var streamVar = streamDecl as Variable;
1075  if (streamVar != null)
1076  {
1077  var variable = new Variable(streamVar.Type, streamVar.Name) { Span = streamVar.Span };
1078  if (useSem)
1079  foreach (var qualifier in streamVar.Qualifiers.OfType<Semantic>())
1080  variable.Qualifiers |= qualifier;
1081 
1082  if (useSem && addAutoSem)
1083  {
1084  var semantic = variable.Qualifiers.Values.OfType<Semantic>().FirstOrDefault();
1085  if (semantic == null)
1086  variable.Qualifiers |= new Semantic(variable.Name.Text.ToUpper() + "_SEM");
1087  }
1088 
1089  tempStruct.Fields.Add(variable);
1090  }
1091  }
1092  return tempStruct;
1093  }
1094 
1095  /// <summary>
1096  /// Creates an intermediate structure given the stream usage
1097  /// </summary>
1098  /// <param name="streamStageUsage">the StreamStageUsage</param>
1099  /// <param name="stageName">the name of the stage</param>
1100  /// <returns>the intermediate stream structure</returns>
1101  private static StructType CreateIntermediateStructType(StreamStageUsage streamStageUsage, string stageName)
1102  {
1103  var tempList = new List<IDeclaration>();
1104  tempList.AddRange(streamStageUsage.InStreamList);
1105  tempList.AddRange(streamStageUsage.InterStreamList);
1106  tempList.AddRange(streamStageUsage.OutStreamList);
1107  return CreateStreamStructure(tempList.Distinct().ToList(), stageName + "_STREAMS", false, false);
1108  }
1109 
1110  /// <summary>
1111  /// Creates an declaration for this structure
1112  /// </summary>
1113  /// <param name="structType">the structure</param>
1114  /// <param name="structVarName">the name of the variable</param>
1115  /// <returns>the declaration statement</returns>
1116  private static DeclarationStatement CreateStructInit(StructType structType, string structVarName)
1117  {
1118  return new DeclarationStatement(
1119  new Variable(new TypeName(structType.Name), structVarName)
1120  {
1121  InitialValue = new CastExpression { From = new LiteralExpression(0), Target = new TypeName(structType.Name) }
1122  });
1123  }
1124 
1125  #endregion
1126  }
1127 
1129  {
1130  Vertex,
1131  Hull,
1132  Constant,
1133  Domain,
1134  Geometry,
1135  Pixel,
1136  Compute,
1137  None
1138  }
1139 
1141  {
1142  public PdxShaderStage ShaderStage = PdxShaderStage.None;
1143  public List<IDeclaration> InStreamList = new List<IDeclaration>();
1144  public List<IDeclaration> InterStreamList = new List<IDeclaration>();
1145  public List<IDeclaration> OutStreamList = new List<IDeclaration>();
1146  }
1147 }
Describes a binary expression.
Specialized ParameterQualifier for Hlsl.
static readonly ScalarType Int
Scalar int.
Definition: ScalarType.cs:37
UnaryOperator
Unary operator used in all binary expressions (except assignment expression).
SiliconStudio.Shaders.Ast.ParameterQualifier ParameterQualifier
A typeless reference.
Definition: TypeName.cs:10
SiliconStudio.Core.Diagnostics.LoggerResult LoggerResult
string Text
Gets or sets the name.
Definition: Identifier.cs:77
BinaryOperator
Binary operator used in all binary expressions (except assignment expression).
A class to collect parsing/expression messages.
Definition: LoggerResult.cs:13
virtual IEnumerable< Node > Childrens()
Gets the child nodes.
Definition: Node.cs:127
AssignmentOperator
Assignment operator used in assignment expression (a = b) or statements (a = b;)
A method definition with a body of statements.
_In_ size_t count
Definition: DirectXTexP.h:174
A variable declaration.
Definition: Variable.cs:11
Base root class for all statements.
Definition: Statement.cs:11
A single parameter declaration.
Definition: Parameter.cs:10
A member reference in the form {this}.{Name}
StatementList Body
Gets or sets the list of statements.
SiliconStudio.Shaders.Ast.SourceSpan SourceSpan
Definition: Node.cs:8
List< Parameter > Parameters
Gets or sets the parameters.
ShaderStage
Enum to specify shader stage.
Definition: ShaderStage.cs:12
Identifier Name
Gets or sets the type name.
Definition: TypeBase.cs:77
List< Variable > Fields
Gets or sets the fields.
Definition: StructType.cs:34
static readonly SiliconStudio.Shaders.Ast.StorageQualifier Clone
Clone keyword (clone).
A field of a struct.
Definition: Literal.cs:13