Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
ShaderLoader.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 #if PARADOX_EFFECT_COMPILER
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 using System.Text;
8 using System.Text.RegularExpressions;
9 
10 using SiliconStudio.Core.Extensions;
11 using SiliconStudio.Core.Storage;
12 using SiliconStudio.Paradox.Shaders.Parser.Ast;
13 using SiliconStudio.Paradox.Shaders.Parser.Grammar;
14 using SiliconStudio.Paradox.Shaders.Parser.Utility;
15 using SiliconStudio.Shaders;
16 using SiliconStudio.Shaders.Ast;
17 using SiliconStudio.Shaders.Ast.Hlsl;
18 using SiliconStudio.Shaders.Parser;
19 using SiliconStudio.Shaders.Utility;
20 
21 namespace SiliconStudio.Paradox.Shaders.Parser.Mixins
22 {
23  /// <summary>
24  /// Provides methods for loading a <see cref="ShaderClassType"/>.
25  /// </summary>
26  public class ShaderLoader
27  {
28  private readonly Dictionary<ShaderSourceKey, ShaderClassType> loadedShaders = new Dictionary<ShaderSourceKey, ShaderClassType>();
29 
30  /// <summary>
31  /// Gets the source manager.
32  /// </summary>
33  /// <value>The source manager.</value>
34  public ShaderSourceManager SourceManager { get; private set; }
35 
36  private readonly static Regex MatchHeader = new Regex(@"\{.*}\s*;", RegexOptions.Singleline | RegexOptions.Compiled);
37 
38  /// <summary>
39  /// Initializes a new instance of the <see cref="ShaderLoader"/> class.
40  /// </summary>
41  /// <param name="sourceManager">The source manager.</param>
42  /// <exception cref="System.ArgumentNullException">sourceManager</exception>
43  public ShaderLoader(ShaderSourceManager sourceManager)
44  {
45  if (sourceManager == null)
46  throw new ArgumentNullException("sourceManager");
47 
48  SourceManager = sourceManager;
49  }
50 
51  /// <summary>
52  /// Deletes the shader cache for the specified shaders.
53  /// </summary>
54  /// <param name="modifiedShaders">The modified shaders.</param>
55  public void DeleteObsoleteCache(HashSet<string> modifiedShaders)
56  {
57  var keysToRemove = new HashSet<ShaderSourceKey>();
58  foreach (var shaderName in modifiedShaders)
59  {
60  foreach (var key in loadedShaders.Keys)
61  {
62  if (key.TypeName == shaderName)
63  keysToRemove.Add(key);
64  }
65  }
66 
67  foreach (var key in keysToRemove)
68  loadedShaders.Remove(key);
69 
70  keysToRemove.Clear();
71 
72  SourceManager.DeleteObsoleteCache(modifiedShaders);
73  }
74 
75  /// <summary>
76  /// Loads the <see cref="ShaderClassType" />.
77  /// </summary>
78  /// <param name="shaderClassSource">The shader class source.</param>
79  /// <param name="shaderMacros">The shader macros.</param>
80  /// <param name="log">The log to output error logs.</param>
81  /// <param name="modifiedShaders">The list of modified shaders.</param>
82  /// <returns>A ShaderClassType or null if there was some errors.</returns>
83  /// <exception cref="System.ArgumentNullException">shaderClassSource</exception>
84  public ShaderClassType LoadClassSource(ShaderClassSource shaderClassSource, SiliconStudio.Shaders.Parser.ShaderMacro[] shaderMacros, LoggerResult log, HashSet<string> modifiedShaders = null)
85  {
86  if (shaderClassSource == null) throw new ArgumentNullException("shaderClassSource");
87 
88  string generics = null;
89  if (shaderClassSource.GenericArguments != null)
90  {
91  generics = "";
92  foreach (var gen in shaderClassSource.GenericArguments)
93  generics += "___" + gen.ToString();
94  }
95  var shaderClassType = LoadShaderClass(shaderClassSource.ClassName, generics, log, shaderMacros, modifiedShaders);
96 
97  if (shaderClassType == null)
98  return null;
99 
100  // Instantiate generic class
101  if (shaderClassSource.GenericArguments != null)
102  {
103  if (shaderClassType.IsInstanciated)
104  return shaderClassType;
105 
106  if (shaderClassSource.GenericArguments.Length != shaderClassType.ShaderGenerics.Count)
107  {
108  log.Error(ParadoxMessageCode.WrongGenericNumber, shaderClassType.Span, shaderClassSource.ClassName);
109  return null;
110  }
111 
112  // check the name of the generics
113  foreach (var generic in shaderClassType.ShaderGenerics)
114  {
115  foreach (var genericCompare in shaderClassType.ShaderGenerics.Where(x => x != generic))
116  {
117  if (generic.Name.Text == genericCompare.Name.Text)
118  log.Error(ParadoxMessageCode.SameNameGenerics, generic.Span, generic, genericCompare, shaderClassSource.ClassName);
119  }
120  }
121 
122  if (log.HasErrors)
123  return null;
124 
125  var className = GenerateGenericClassName(shaderClassSource);
126  shaderClassType.Name = new Identifier(className);
127  var genericAssociation = CreateGenericAssociation(shaderClassType.ShaderGenerics, shaderClassSource.GenericArguments);
128  var identifierGenerics = GenerateIdentifierFromGenerics(genericAssociation);
129  var expressionGenerics = GenerateGenericsExpressionValues(shaderClassType.ShaderGenerics, shaderClassSource.GenericArguments);
130  ParadoxClassInstanciator.Instanciate(shaderClassType, expressionGenerics, identifierGenerics, log);
131  shaderClassType.ShaderGenerics.Clear();
132  shaderClassType.IsInstanciated = true;
133  }
134  return shaderClassType;
135  }
136 
137  Dictionary<string, object> CreateGenericAssociation(List<Variable> genericParameters, object[] genericArguments)
138  {
139  var result = new Dictionary<string, object>();
140  for (var i = 0; i < genericParameters.Count; ++i)
141  {
142  result.Add(genericParameters[i].Name.Text, genericArguments[i]);
143  }
144  return result;
145  }
146 
147  Dictionary<string, Identifier> GenerateIdentifierFromGenerics(Dictionary<string, object> generics)
148  {
149  var result = new Dictionary<string, Identifier>();
150  foreach (var genericPair in generics)
151  {
152  var generic = genericPair.Value;
153  if (generic is Identifier)
154  result.Add(genericPair.Key, (Identifier)generic);
155  else //if (generic is string)
156  {
157  var stringGeneric = generic.ToString();// generic as string;
158  var stringParts = stringGeneric.Split('.');
159  if (stringParts.Length == 1)
160  result.Add(genericPair.Key, new Identifier(stringGeneric));
161  else
162  {
163  var dotIdentifier = new IdentifierDot();
164  dotIdentifier.Identifiers = stringParts.Select(x => new Identifier(x)).ToList();
165  result.Add(genericPair.Key, dotIdentifier);
166  }
167  }
168  //else
169  // throw new Exception("Unsupported generic.");
170  }
171  return result;
172  }
173 
174  private Dictionary<string, Expression> GenerateGenericsExpressionValues(List<Variable> genericParameters, object[] genericArguments)
175  {
176  var result = new Dictionary<string, Expression>();
177 
178  if (genericArguments.Length > 0)
179  {
180  string allGenerics = "";
181  foreach (var generic in genericArguments)
182  allGenerics += "," + generic.ToString();
183  allGenerics = allGenerics.Substring(1);
184 
185  var node = CreateExpressionFromString(allGenerics);
186 
187  if (node is ExpressionList)
188  {
189  var nodeList = (ExpressionList)node;
190  if (nodeList.Count != genericArguments.Length)
191  throw new Exception("mismatch generic length after parsing");
192 
193  for (var i = 0; i < genericArguments.Length; ++i)
194  result.Add(genericParameters[i].Name.Text, nodeList[i]);
195  }
196  else
197  {
198  if (genericArguments.Length != 1)
199  throw new Exception("mismatch generic length after parsing");
200  result.Add(genericParameters[0].Name.Text, node);
201  }
202  }
203  return result;
204  }
205 
206  Expression CreateExpressionFromString(string name)
207  {
208  // TODO: catch errors
209  var result = ShaderParser.GetParser<ParadoxGrammar>(ShaderParser.GetGrammar<ParadoxGrammar>().ExpressionNonTerminal).Parser.Parse(name, "");
210  return (Expression)result.Root.AstNode;
211  }
212 
213  private ShaderClassType LoadShaderClass(string type, string generics, LoggerResult log, SiliconStudio.Shaders.Parser.ShaderMacro[] macros = null, HashSet<string> modifiedShaders = null)
214  {
215  if (type == null) throw new ArgumentNullException("type");
216 
217  var shaderSourceKey = new ShaderSourceKey(type, generics, macros);
218 
219  lock (loadedShaders)
220  {
221  // Already instantiated
222  ShaderClassType shaderClass;
223 
224  if (loadedShaders.TryGetValue(shaderSourceKey, out shaderClass))
225  {
226  return shaderClass;
227  }
228 
229  // Load file
230  var shaderSource = SourceManager.LoadShaderSource(type, modifiedShaders);
231 
232  // TODO USE ORIGINAL SOURCE PATH and not to object database path
233  var preprocessedSource = PreProcessor.Run(shaderSource.Source, shaderSource.Path, macros);
234 
235  byte[] byteArray = Encoding.ASCII.GetBytes(preprocessedSource);
236  var hashPreprocessSource = ObjectId.FromBytes(byteArray);
237 
238  // Compile
239  var parsingResult = ParadoxShaderParser.TryParse(preprocessedSource, shaderSource.Path);
240  parsingResult.CopyTo(log);
241 
242  if (parsingResult.HasErrors)
243  {
244  return null;
245  }
246 
247  var shader = parsingResult.Shader;
248 
249  // As shaders can be embedded in namespaces, get only the shader class and make sure there is only one in a pdxsl.
250  var shaderClassTypes = GetShaderClassTypes(shader.Declarations).ToList();
251  if (shaderClassTypes.Count != 1)
252  {
253  throw new InvalidOperationException(string.Format("Shader [{0}] must contain only a single Shader class type intead of [{1}]", type, shaderClassTypes.Count));
254  }
255 
256  shaderClass = shaderClassTypes.First();
257  shaderClass.SourcePath = shaderSource.Path;
258  shaderClass.SourceHash = shaderSource.Hash;
259  shaderClass.PreprocessedSourceHash = hashPreprocessSource;
260  shaderClass.IsInstanciated = false;
261 
262  // TODO: We should not use Console. Change the way we log things here
263  Console.WriteLine("Loading Shader {0}{1}", type, macros != null && macros.Length > 0 ? String.Format("<{0}>", string.Join(", ", macros)) : string.Empty);
264 
265  if (shaderClass.Name.Text != type)
266  {
267  throw new InvalidOperationException(string.Format("Unable to load shader [{0}] not maching class name [{1}]", type, shaderClass.Name.Text));
268  }
269 
270  // Only full version are stored
271  loadedShaders.Add(shaderSourceKey, shaderClass);
272 
273  return shaderClass;
274  }
275  }
276 
277  public ShaderClassType ParseSource(string shaderSource, LoggerResult log)
278  {
279  var parsingResult = ParadoxShaderParser.TryParse(shaderSource, null);
280  parsingResult.CopyTo(log);
281 
282  if (parsingResult.HasErrors)
283  {
284  return null;
285  }
286 
287  var shader = parsingResult.Shader;
288  var shaderClass = shader.Declarations.OfType<ShaderClassType>().Single();
289  shaderClass.SourcePath = null;// shaderSource.Path;
290  shaderClass.SourceHash = ObjectId.Empty;// shaderSource.Hash;
291  shaderClass.PreprocessedSourceHash = ObjectId.Empty; // hashPreprocessSource;
292  shaderClass.IsInstanciated = false;
293 
294  return shaderClass;
295  }
296 
297  public bool ClassExists(string className)
298  {
299  return SourceManager.IsClassExists(className);
300  }
301 
302  private static Dictionary<string, string> GenerateGenericMapping(ShaderClassType shaderClassType, IList<object> genericParameters)
303  {
304  var identifierGenericClass = shaderClassType.GenericParameters;
305  if (identifierGenericClass.Count != genericParameters.Count)
306  throw new InvalidOperationException("Number of parameters in this generic instantiation doesn't match.");
307 
308  // Build generic mapping (i.e. T => RealType, U => RealType2)
309  return identifierGenericClass
310  .Select((value, index) => new { value, index })
311  .ToDictionary(x => x.value.Name.Text, x => genericParameters[x.index].ToString());
312  }
313 
314  private static string GenerateGenericClassName(ShaderClassType shaderClassType)
315  {
316  // Generate class name
317  return shaderClassType.Name.Text + (shaderClassType.GenericParameters == null ? string.Empty : "_" + string.Join("_", shaderClassType.GenericParameters.Select(x => x.ToString().Replace('.', '_'))));
318  }
319 
320  private static string GenerateGenericClassName(ShaderClassSource source)
321  {
322  // Generate class name
323  if (source.GenericArguments != null && source.GenericArguments.Length > 0)
324  {
325  var hash = source.GenericArguments[0].ToString().GetHashCode();
326  for (var i = 0; i < source.GenericArguments.Length; ++i)
327  {
328  hash = (hash * 397) ^ source.GenericArguments[i].ToString().GetHashCode();
329  }
330 
331  if (hash < 0)
332  {
333  hash = -hash;
334  return source.ClassName + "_Min" + hash.ToString();
335  }
336 
337  return source.ClassName + "_" + hash.ToString();
338  }
339  return source.ClassName;
340  }
341 
342  private static IEnumerable<ShaderClassType> GetShaderClassTypes(IEnumerable<SiliconStudio.Shaders.Ast.Node> nodes)
343  {
344  foreach (var node in nodes)
345  {
346  var namespaceBlock = node as NamespaceBlock;
347  if (namespaceBlock != null)
348  {
349  foreach (var type in GetShaderClassTypes(namespaceBlock.Body))
350  {
351  yield return type;
352  }
353  }
354  else
355  {
356  var shaderClass = node as ShaderClassType;
357  if (shaderClass != null)
358  {
359  yield return shaderClass;
360  }
361  }
362  }
363  }
364 
365  private class ShaderSourceKey : IEquatable<ShaderSourceKey>
366  {
367  public readonly string TypeName;
368  private readonly string generics;
369  private readonly SiliconStudio.Shaders.Parser.ShaderMacro[] shaderMacros;
370  private readonly int hashCode;
371 
372  public ShaderSourceKey(string typeName, string generics, SiliconStudio.Shaders.Parser.ShaderMacro[] shaderMacros)
373  {
374  this.TypeName = typeName;
375  this.generics = generics;
376  this.shaderMacros = shaderMacros;
377  unchecked
378  {
379  hashCode = ((typeName != null ? typeName.GetHashCode() : 0) * 397) ^ (generics != null ? generics.GetHashCode() : 0);
380  hashCode = (hashCode * 397) ^ (this.shaderMacros != null ? this.shaderMacros.ComputeHash() : 0);
381  }
382  }
383 
384  public bool Equals(ShaderSourceKey other)
385  {
386  return Equals(other.TypeName, TypeName) && Equals(other.generics, generics) && ArrayExtensions.ArraysEqual(other.shaderMacros, shaderMacros);
387  }
388 
389  public override bool Equals(object obj)
390  {
391  if (ReferenceEquals(null, obj)) return false;
392  if (obj.GetType() != typeof(ShaderSourceKey)) return false;
393  return Equals((ShaderSourceKey)obj);
394  }
395 
396  public override int GetHashCode()
397  {
398  return hashCode;
399  }
400  }
401  }
402 }
403 #endif
Gets a single texture view at the specified index in the mip hierarchy and in the array of textures T...
Macro to be used with PreProcessor.
Definition: ShaderMacro.cs:11
SiliconStudio.Core.Diagnostics.LoggerResult LoggerResult