Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ShaderMixinParser.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 using System.Security.Cryptography.X509Certificates;
7 
8 using SiliconStudio.Paradox.Shaders.Parser.Ast;
9 using SiliconStudio.Paradox.Shaders.Parser.Mixins;
10 using SiliconStudio.Paradox.Shaders.Parser.Performance;
11 using SiliconStudio.Shaders.Analysis.Hlsl;
12 using SiliconStudio.Shaders.Ast;
13 using SiliconStudio.Shaders.Ast.Hlsl;
14 using SiliconStudio.Shaders.Utility;
15 
16 namespace SiliconStudio.Paradox.Shaders.Parser
17 {
18  /// <summary>
19  /// Parser for mixin.
20  /// </summary>
21  public class ShaderMixinParser
22  {
23  #region Private members
24 
25  /// <summary>
26  /// An Objbect to lock the preprocess step (virtual tables building etc.).
27  /// </summary>
28  private readonly static Object PreprocessLock = new Object();
29 
30  /// <summary>
31  /// An Objbect to lock the semantic analysis step.
32  /// </summary>
33  private readonly static Object SemanticAnalyzerLock = new Object();
34 
35  /// <summary>
36  /// The CloneContext with the Hlsl classes and types
37  /// </summary>
38  private readonly CloneContext hlslCloneContext = new CloneContext();
39 
40  /// <summary>
41  /// The library containing all the shaders
42  /// </summary>
43  private readonly ParadoxShaderLibrary shaderLibrary;
44 
45  #endregion
46 
47  #region Public members
48 
49  /// <summary>
50  /// The shader source manager.
51  /// </summary>
53 
54  #endregion
55 
56  #region Constructor
57 
58  /// <summary>
59  /// Initializes a new instance of the <see cref="ShaderMixinParser"/> class.
60  /// </summary>
62  {
63  SourceManager = new ShaderSourceManager();
64  var shaderLoader = new ShaderLoader(SourceManager);
65 
66  if (shaderLibrary == null)
67  {
68  shaderLibrary = new ParadoxShaderLibrary(shaderLoader);
69  }
70 
71  // Create the clone context with the instances of Hlsl classes
72  HlslSemanticAnalysis.FillCloneContext(hlslCloneContext);
73  }
74 
75  #endregion
76 
77  #region Public method
78 
79  /// <summary>
80  /// Deletes the shader cache for the specified shaders.
81  /// </summary>
82  /// <param name="modifiedShaders">The modified shaders.</param>
83  public void DeleteObsoleteCache(HashSet<string> modifiedShaders)
84  {
85  shaderLibrary.DeleteObsoleteCache(modifiedShaders);
86  }
87 
88  /// <summary>
89  /// Mixes shader parts to produces a single HLSL file shader.
90  /// </summary>
91  /// <param name="shaderMixinSource">The shader source.</param>
92  /// <param name="macros">The shader perprocessor macros.</param>
93  /// <param name="modifiedShaders">The list of modified shaders.</param>
94  /// <returns>The combined shader in AST form.</returns>
95  public ShaderMixinParsingResult Parse(ShaderMixinSource shaderMixinSource, Paradox.Shaders.ShaderMacro[] macros = null, HashSet<string> modifiedShaders = null)
96  {
97  //SemanticPerformance.Reset();
98  //PerformanceLogger.Reset();
99  //MixPerformance.Reset();
100  //GenerateShaderPerformance.Reset();
101  //StreamCreatorPerformance.Reset();
102 
103  // updates the list of modified shaders.
104  shaderLibrary.ModifiedShaders = modifiedShaders;
105 
106  // Creates a parsing result
107  var parsingResult = new ShaderMixinParsingResult();
108 
110  if (macros == null)
111  {
112  macrosParser = new SiliconStudio.Shaders.Parser.ShaderMacro[0];
113  }
114  else
115  {
116  macrosParser = new SiliconStudio.Shaders.Parser.ShaderMacro[macros.Length];
117  for (var i = 0; i < macros.Length; ++i)
118  macrosParser[i] = new SiliconStudio.Shaders.Parser.ShaderMacro(macros[i].Name, macros[i].Definition);
119  }
120  //PerformanceLogger.Start(PerformanceStage.Global);
121 
122  // ----------------------------------------------------------
123  // Load all shaders
124  // ----------------------------------------------------------
125  HashSet<ModuleMixinInfo> mixinsToAnalyze;
126  lock (shaderLibrary)
127  {
128  //PerformanceLogger.Start(PerformanceStage.Loading);
129  mixinsToAnalyze = shaderLibrary.LoadShaderSource(shaderMixinSource, macrosParser);
130  //PerformanceLogger.Stop(PerformanceStage.Loading);
131  }
132 
133  // Extract all ModuleMixinInfo and check for any errors
134  var allMixinInfos = new HashSet<ModuleMixinInfo>();
135  foreach (var moduleMixinInfo in mixinsToAnalyze)
136  {
137  allMixinInfos.UnionWith(moduleMixinInfo.MinimalContext);
138  }
139  foreach (var moduleMixinInfo in allMixinInfos)
140  {
141  moduleMixinInfo.Log.CopyTo(parsingResult);
142 
143  var ast = moduleMixinInfo.MixinAst;
144  var shaderClassSource = moduleMixinInfo.ShaderSource as ShaderClassSource;
145  if (ast != null && shaderClassSource != null)
146  {
147  var sourcePath = SourceManager.FindFilePath(shaderClassSource.ClassName);
148  if (sourcePath == null)
149  throw new InvalidOperationException(string.Format("Can't find source path for class {0}", shaderClassSource.ClassName));
150  parsingResult.HashSources[sourcePath] = ast.SourceHash;
151  }
152  }
153 
154  // Return directly if there was any errors
155  if (parsingResult.HasErrors)
156  return parsingResult;
157 
158  // ----------------------------------------------------------
159  // Perform Type Analysis
160  // ----------------------------------------------------------
161  //PerformanceLogger.Start(PerformanceStage.TypeAnalysis);
162  var context = GetCompilationContext(mixinsToAnalyze, parsingResult);
163  //PerformanceLogger.Stop(PerformanceStage.TypeAnalysis);
164 
165  // Return directly if there was any errors
166  if (parsingResult.HasErrors)
167  return parsingResult;
168 
169  lock (SemanticAnalyzerLock)
170  {
171  //PerformanceLogger.Start(PerformanceStage.SemanticAnalysis);
172  //SemanticPerformance.Start(SemanticStage.Global);
173  foreach (var mixin in mixinsToAnalyze)
174  context.Analyze(mixin);
175  //SemanticPerformance.Pause(SemanticStage.Global);
176  //PerformanceLogger.Stop(PerformanceStage.SemanticAnalysis);
177  }
178 
179  // Return directly if there was any errors
180  if (parsingResult.HasErrors)
181  return parsingResult;
182 
183  // Update the clone context in case new instances of classes are created
184  lock (hlslCloneContext)
185  {
186  HlslSemanticAnalysis.UpdateCloneContext(hlslCloneContext);
187  }
188 
189  // only clone once the stage classes
190  var mixCloneContext = new CloneContext(hlslCloneContext);
191  foreach (var mixinInfo in mixinsToAnalyze)
192  {
193  foreach (var mixin in mixinInfo.Mixin.MinimalContext.Where(x => x.StageOnlyClass))
194  {
195  mixin.DeepClone(mixCloneContext);
196  }
197  }
198 
199  // ----------------------------------------------------------
200  // Perform Shader Mixer
201  // ----------------------------------------------------------
202  var externDict = new Dictionary<Variable, List<ModuleMixin>>();
203  var finalModuleList = BuildCompositionsDictionary(shaderMixinSource, externDict, context, mixCloneContext);
204  //PerformanceLogger.Stop(PerformanceStage.DeepClone);
205  var mixinDictionary = BuildMixinDictionary(finalModuleList);
206 
207  if (finalModuleList != null)
208  {
209  var finalModule = finalModuleList[0];
210  //PerformanceLogger.Start(PerformanceStage.Mix);
211  var mixer = new ParadoxShaderMixer(finalModule, parsingResult, mixinDictionary, externDict, new CloneContext(mixCloneContext));
212  mixer.Mix();
213  //PerformanceLogger.Stop(PerformanceStage.Mix);
214 
215  // Return directly if there was any errors
216  if (parsingResult.HasErrors)
217  return parsingResult;
218 
219  var finalShader = mixer.GetMixedShader();
220 
221  parsingResult.Reflection = new EffectReflection();
222  var pdxShaderLinker = new ShaderLinker(parsingResult);
223  pdxShaderLinker.Run(finalShader);
224 
225  // Return directly if there was any errors
226  if (parsingResult.HasErrors)
227  return parsingResult;
228 
229  // Find all entry points
230  // TODO: make this configurable by CompileParameters
231  foreach (var stage in new[] {ShaderStage.Compute, ShaderStage.Vertex, ShaderStage.Hull, ShaderStage.Domain, ShaderStage.Geometry, ShaderStage.Pixel})
232  {
233  var entryPoint = finalShader.Declarations.OfType<MethodDefinition>().FirstOrDefault(f => f.Attributes.OfType<AttributeDeclaration>().Any(a => a.Name == "EntryPoint" && (string)a.Parameters[0].Value == stage.ToString()));
234 
235  if (entryPoint == null)
236  {
237  continue;
238  }
239 
240  parsingResult.EntryPoints[stage] = entryPoint.Name.Text;
241 
242  // When this is a compute shader, there is no need to scan other stages
243  if (stage == ShaderStage.Compute)
244  break;
245  }
246 
247  var typeCleaner = new ParadoxShaderCleaner();
248  typeCleaner.Run(finalShader);
249 
250  //PerformanceLogger.Stop(PerformanceStage.Global);
251 
252  //PerformanceLogger.PrintLastResult();
253  //SemanticPerformance.PrintResult();
254  //MixPerformance.PrintResult();
255  //GenerateShaderPerformance.PrintResult();
256  //StreamCreatorPerformance.PrintResult();
257  //ShaderLoader.PrintTime();
258 
259  //PerformanceLogger.WriteOut(52);
260 
261  parsingResult.Shader = finalShader;
262  }
263 
264  return parsingResult;
265  }
266 
267  #endregion
268 
269  #region Internal methods
270 
271  internal ModuleMixinInfo GetMixin(string mixinName)
272  {
273  return shaderLibrary.MixinInfos.FirstOrDefault(x => x.MixinGenericName == mixinName);
274  }
275 
276  #endregion
277 
278  #region Private methods
279 
280  /// <summary>
281  /// create the context for each composition by cloning their dependencies
282  /// </summary>
283  /// <param name="shaderSource">the entry ShaderSource (root)</param>
284  /// <param name="dictionary">the ouputed compositions</param>
285  /// <param name="compilationContext">the compilation context</param>
286  /// <param name="cloneContext">The clone context.</param>
287  /// <returns>a list of all the needed mixins</returns>
288  private List<ModuleMixin> BuildCompositionsDictionary(ShaderSource shaderSource, Dictionary<Variable, List<ModuleMixin>> dictionary, ShaderCompilationContext compilationContext, CloneContext cloneContext)
289  {
290  if (shaderSource is ShaderMixinSource)
291  {
292  var shaderMixinSource = shaderSource as ShaderMixinSource;
293 
294  var finalModule = compilationContext.GetModuleMixinFromShaderSource(shaderSource);
295 
296  //PerformanceLogger.Start(PerformanceStage.DeepClone);
297  finalModule = finalModule.DeepClone(new CloneContext(cloneContext));
298  //PerformanceLogger.Pause(PerformanceStage.DeepClone);
299 
300  foreach (var composition in shaderMixinSource.Compositions)
301  {
302  //look for the key
303  var foundVars = finalModule.FindAllVariablesByName(composition.Key).Where(value => value.Variable.Qualifiers.Contains(ParadoxStorageQualifier.Compose)).ToList();
304 
305  if (foundVars.Count > 0)
306  {
307  Variable foundVar = foundVars[0].Variable;
308  var moduleMixins = BuildCompositionsDictionary(composition.Value, dictionary, compilationContext, cloneContext);
309  if (moduleMixins == null)
310  return null;
311 
312  dictionary.Add(foundVar, moduleMixins);
313  }
314  else
315  {
316  // TODO: log an error?
317  }
318  }
319  return new List<ModuleMixin> { finalModule };
320  }
321 
322 
323  if (shaderSource is ShaderClassSource)
324  {
325  var finalModule = compilationContext.GetModuleMixinFromShaderSource(shaderSource);
326 
327  //PerformanceLogger.Start(PerformanceStage.DeepClone);
328  finalModule = finalModule.DeepClone(new CloneContext(cloneContext));
329  //PerformanceLogger.Pause(PerformanceStage.DeepClone);
330 
331  return new List<ModuleMixin> { finalModule };
332  }
333 
334  if (shaderSource is ShaderArraySource)
335  {
336  var shaderArraySource = shaderSource as ShaderArraySource;
337  var compositionArray = new List<ModuleMixin>();
338  foreach (var shader in shaderArraySource.Values)
339  {
340  var mixin = BuildCompositionsDictionary(shader, dictionary, compilationContext, cloneContext);
341  if (mixin == null)
342  return null;
343  compositionArray.AddRange(mixin);
344  }
345  return compositionArray;
346  }
347 
348  return null;
349  }
350 
351  /// <summary>
352  /// Get a compilation context based on the macros
353  /// </summary>
354  /// <param name="mixinToAnalyze">List of mixin to analyze</param>
355  /// <param name="log">The log.</param>
356  /// <returns>the correct compilation context</returns>
357  private ShaderCompilationContext GetCompilationContext(IEnumerable<ModuleMixinInfo> mixinToAnalyze, LoggerResult log)
358  {
359  var mixinInfos = new HashSet<ModuleMixinInfo>();
360  foreach (var mixin in mixinToAnalyze)
361  mixinInfos.UnionWith(mixin.MinimalContext);
362 
363  var context = new ShaderCompilationContext(log);
364  context.Preprocess(mixinInfos);
365  return context;
366  }
367 
368  /// <summary>
369  /// Build a dictionary of mixins
370  /// </summary>
371  /// <param name="finalMixins">a list of mixins</param>
372  /// <returns>a dictionary of all the necessary mixins</returns>
373  private Dictionary<string, ModuleMixin> BuildMixinDictionary(IEnumerable<ModuleMixin> finalMixins)
374  {
375  var allMixins = new HashSet<ModuleMixin>();
376  foreach (var mixin in finalMixins)
377  {
378  if (allMixins.All(x => x.MixinName != mixin.MixinName))
379  allMixins.Add(mixin);
380  }
381 
382  return allMixins.ToDictionary(x => x.MixinName, x => x);
383  }
384 
385  #endregion
386  }
387 }
static readonly SiliconStudio.Shaders.Ast.StorageQualifier Compose
Compose keyword (compose).
function a
A mixin performing a combination of ShaderClassSource and other mixins.
Macro to be used with PreProcessor.
Definition: ShaderMacro.cs:11
ShaderMixinParsingResult Parse(ShaderMixinSource shaderMixinSource, Paradox.Shaders.ShaderMacro[] macros=null, HashSet< string > modifiedShaders=null)
Mixes shader parts to produces a single HLSL file shader.
A class to collect parsing/expression messages.
Definition: LoggerResult.cs:13
ShaderMixinParser()
Initializes a new instance of the ShaderMixinParser class.
void DeleteObsoleteCache(HashSet< string > modifiedShaders)
Deletes the shader cache for the specified shaders.
A method definition with a body of statements.
A variable declaration.
Definition: Variable.cs:11
The reflection data describing the parameters of a shader.
Provides a dictionary of cloned values, where the [key] is the original object and [value] the new ob...
ShaderStage
Enum to specify shader stage.
Definition: ShaderStage.cs:12
readonly ShaderSourceManager SourceManager
The shader source manager.
ShaderMacro(string name, object definition)
Initializes a new instance of the ShaderMacro struct.
Definition: ShaderMacro.cs:18