4 using System.Collections.Generic;
8 using SiliconStudio.Core.Extensions;
9 using SiliconStudio.Core.Storage;
10 using SiliconStudio.Paradox.Shaders.Parser.Ast;
11 using SiliconStudio.Paradox.Shaders.Parser.Utility;
12 using SiliconStudio.Shaders.Ast;
13 using SiliconStudio.Shaders.Ast.Hlsl;
14 using SiliconStudio.Shaders.Utility;
16 namespace SiliconStudio.
Paradox.Shaders.Parser.Mixins
18 internal class ParadoxShaderLibrary
22 public delegate
ShaderClassType LoadClassSourceDelegate(ShaderClassSource shaderClassSource, SiliconStudio.Shaders.Parser.ShaderMacro[] shaderMacros, out
ObjectId hash, out
ObjectId hashPreprocessSource);
26 #region Public members
31 public HashSet<ModuleMixinInfo> MixinInfos =
new HashSet<ModuleMixinInfo>();
36 public ShaderLoader ShaderLoader {
get;
private set; }
46 public HashSourceCollection SourceHashes =
new HashSourceCollection();
51 public HashSet<string> ModifiedShaders =
new HashSet<string>();
55 #region Private members
57 private int lastMixIndex = 0;
62 private readonly Dictionary<string, List<ModuleMixinInfo>> mapMacrosToMixins =
new Dictionary<string, List<ModuleMixinInfo>>();
68 public ParadoxShaderLibrary(ShaderLoader loader)
70 ShaderLoader = loader;
75 #region Public methods
83 public HashSet<ModuleMixinInfo> LoadShaderSource(ShaderSource shaderSource, SiliconStudio.Shaders.Parser.ShaderMacro[] macros)
85 var mixinsToAnalyze =
new HashSet<ModuleMixinInfo>();
86 ExtendLibrary(shaderSource, macros, mixinsToAnalyze);
87 ReplaceMixins(mixinsToAnalyze);
88 return mixinsToAnalyze;
95 public void DeleteObsoleteCache(HashSet<string> modifiedShaders)
97 var mixinsToDelete =
new HashSet<ModuleMixinInfo>();
99 foreach (var shaderName
in modifiedShaders)
102 foreach (var mixin
in MixinInfos)
104 if (mixin.MixinName == shaderName)
105 mixinsToDelete.Add(mixin);
108 foreach (var dep
in mixin.MinimalContext)
110 if (dep.MixinName == shaderName)
111 mixinsToDelete.Add(mixin);
117 SourceHashes.Remove(shaderName);
121 foreach (var mixin
in mixinsToDelete)
123 MixinInfos.Remove(mixin);
126 foreach (var macroMap
in mapMacrosToMixins)
127 macroMap.Value.Remove(mixin);
130 mixinsToDelete.Clear();
132 ShaderLoader.DeleteObsoleteCache(modifiedShaders);
137 #region Private methods
144 private void ExtendLibrary(ShaderSource shaderSource, SiliconStudio.Shaders.Parser.ShaderMacro[] macros, HashSet<ModuleMixinInfo> mixinToAnalyze)
146 if (shaderSource is ShaderMixinSource)
148 var newMacros = MergeMacroSets((ShaderMixinSource)shaderSource, macros);
149 mixinToAnalyze.Add(GetModuleMixinInfo(shaderSource, newMacros));
150 foreach (var composition
in ((ShaderMixinSource)shaderSource).Compositions)
151 ExtendLibrary(composition.Value, newMacros, mixinToAnalyze);
153 else if (shaderSource is ShaderClassSource)
154 mixinToAnalyze.Add(GetModuleMixinInfo(shaderSource, macros));
155 else if (shaderSource is ShaderArraySource)
157 foreach (var shader
in ((ShaderArraySource)shaderSource).Values)
158 ExtendLibrary(shader, macros, mixinToAnalyze);
169 private ModuleMixinInfo GetModuleMixinInfo(ShaderSource shaderSource, SiliconStudio.Shaders.Parser.ShaderMacro[] macros,
string macrosString = null)
174 if (macrosString == null)
176 macrosString = string.Join(
",", macros.OrderBy(x => x.Name));
179 List<ModuleMixinInfo> context;
180 if (!mapMacrosToMixins.TryGetValue(macrosString, out context))
182 context =
new List<ModuleMixinInfo>();
183 mapMacrosToMixins.Add(macrosString, context);
186 var mixinInfo = context.FirstOrDefault(x => x.AreEqual(shaderSource, macros));
187 if (mixinInfo == null)
189 mixinInfo = BuildMixinInfo(shaderSource, macros);
191 if (mixinInfo.Instanciated)
193 MixinInfos.Add(mixinInfo);
194 mapMacrosToMixins[macrosString].Add(mixinInfo);
196 mixinInfo.MinimalContext.Add(mixinInfo);
198 if (!mixinInfo.Log.HasErrors)
200 LoadNecessaryShaders(mixinInfo, macros, macrosString);
202 mixinInfo.MinimalContext =
new HashSet<ModuleMixinInfo>(mixinInfo.MinimalContext.Distinct());
213 private void ReplaceMixins(HashSet<ModuleMixinInfo> mixinInfos)
215 foreach (var mixinInfo
in mixinInfos)
216 CheckMixinForReplacement(mixinInfo);
223 private void CheckMixinForReplacement(ModuleMixinInfo mixinInfo)
227 if (mixinInfo.ReplacementChecked)
231 mixinInfo.MinimalContext.Where(x => x != mixinInfo).ForEach(CheckMixinForReplacement);
233 foreach (var replaceCandidateMixinInfo
in MixinInfos.Where(x => x != mixinInfo && x.ShaderSource.Equals(mixinInfo.ShaderSource) && x.HashPreprocessSource == mixinInfo.HashPreprocessSource))
235 if (replaceCandidateMixinInfo != null && replaceCandidateMixinInfo.Mixin.DependenciesStatus !=
AnalysisStatus.None)
237 if (replaceCandidateMixinInfo.Mixin.MinimalContext != null)
239 var noNeedToReplaced = replaceCandidateMixinInfo.Mixin.MinimalContext
240 .Where(dep => dep != replaceCandidateMixinInfo.Mixin)
241 .
All(dep => mixinInfo.MinimalContext.FirstOrDefault(x => x.Mixin == dep) != null);
242 if (noNeedToReplaced)
244 mixinInfo.Mixin = replaceCandidateMixinInfo.Mixin;
245 mixinInfo.MixinAst = replaceCandidateMixinInfo.MixinAst;
246 mixinInfo.MixinGenericName = replaceCandidateMixinInfo.MixinGenericName;
253 mixinInfo.ReplacementChecked =
true;
262 private ModuleMixinInfo BuildMixinInfo(ShaderSource shaderSource, SiliconStudio.Shaders.Parser.ShaderMacro[] macros)
264 ModuleMixinInfo mixinInfo = null;
266 if (shaderSource is ShaderClassSource)
268 var shaderClassSource = shaderSource as ShaderClassSource;
269 mixinInfo =
new ModuleMixinInfo { ShaderSource = shaderClassSource, Macros = macros };
270 LoadMixinFromClassSource(mixinInfo);
272 else if (shaderSource is ShaderMixinSource)
274 var shaderMixinSource = shaderSource as ShaderMixinSource;
276 var shaderName =
"Mix" + lastMixIndex;
279 foreach (var classSource
in shaderMixinSource.Mixins)
282 if (classSource.GenericArguments != null && classSource.GenericArguments.Length > 0)
283 name =
new IdentifierGeneric(classSource.ClassName, classSource.GenericArguments.Select(x =>
new Identifier(x.ToString())).ToArray());
287 fakeAst.BaseClasses.Add(
new TypeName(name));
290 mixinInfo =
new ModuleMixinInfo
292 MixinGenericName = shaderName,
295 ShaderSource = shaderSource,
296 SourceHash = ObjectId.FromBytes(Encoding.UTF8.GetBytes(shaderName)),
308 private void LoadMixinFromClassSource(ModuleMixinInfo mixinInfo)
310 var classSource = (ShaderClassSource)mixinInfo.ShaderSource;
312 var shaderClass = ShaderLoader.LoadClassSource(classSource, mixinInfo.Macros, mixinInfo.Log, ModifiedShaders);
315 if (shaderClass == null)
318 shaderClass = shaderClass.DeepClone();
320 if (shaderClass.ShaderGenerics != null && shaderClass.ShaderGenerics.Count != 0)
321 mixinInfo.Instanciated =
false;
323 mixinInfo.HashPreprocessSource = shaderClass.PreprocessedSourceHash;
324 mixinInfo.SourceHash = shaderClass.SourceHash;
326 if (!SourceHashes.ContainsKey(classSource.ClassName))
327 SourceHashes.Add(classSource.ClassName, shaderClass.SourceHash);
330 if (shaderClass.GenericParameters.Count > 0)
332 if (classSource.GenericArguments == null || classSource.GenericArguments.Length == 0 || shaderClass.GenericParameters.Count > classSource.GenericArguments.Length)
334 mixinInfo.Instanciated =
false;
335 mixinInfo.Log.Error(ParadoxMessageCode.ErrorClassSourceNotInstantiated, shaderClass.Span, classSource.ClassName);
339 ModuleMixinInfo.CleanIdentifiers(shaderClass.GenericParameters.Select(x => x.Name).ToList());
343 mixinInfo.MixinAst = shaderClass;
344 mixinInfo.MixinGenericName = classSource.ClassName;
353 private void LoadNecessaryShaders(ModuleMixinInfo mixinInfo, SiliconStudio.Shaders.Parser.ShaderMacro[] macros,
string macrosString)
355 if (!mixinInfo.Instanciated)
359 var shaderDependencyVisitor =
new ShaderDependencyVisitor(mixinInfo.Log, ShaderLoader.SourceManager);
360 shaderDependencyVisitor.Run(mixinInfo.MixinAst);
362 foreach (var foundClass
in shaderDependencyVisitor.FoundClasses)
364 var classSource =
new ShaderClassSource(foundClass, null);
365 var foundMixinInfo = GetModuleMixinInfo(classSource, macros, macrosString);
366 mixinInfo.MinimalContext.UnionWith(foundMixinInfo.MinimalContext);
369 foreach (var
id in shaderDependencyVisitor.FoundIdentifiers)
371 var genericClass = id.Item1;
372 ModuleMixinInfo.CleanIdentifiers(genericClass.Identifiers);
373 var genericParams = BuildShaderGenericParameters(genericClass);
374 var classSource =
new ShaderClassSource(genericClass.Text, genericParams);
376 var instanciatedClassInfo = GetModuleMixinInfo(classSource, macros, macrosString);
377 mixinInfo.MinimalContext.UnionWith(instanciatedClassInfo.MinimalContext);
379 var newId =
new Identifier(instanciatedClassInfo.MixinName);
381 (id.Item2 as TypeName).Name = newId;
383 (id.Item2 as VariableReferenceExpression).Name = newId;
385 (id.Item2 as MemberReferenceExpression).Member = newId;
391 #region Private static methods
398 private static string[] BuildShaderGenericParameters(
IdentifierGeneric genericClass)
400 var genericParameters =
new List<string>();
402 for (
int i = 0; i < genericClass.Identifiers.Count; ++i)
404 var genericName = GetIdentifierName(genericClass.
Identifiers[i]);
405 genericParameters.Add(genericName);
408 return genericParameters.ToArray();
416 private static string GetIdentifierName(
Identifier identifier)
420 genericName = (identifier as LiteralIdentifier).
Value.Value.ToString();
423 var idDot = identifier as IdentifierDot;
424 genericName = idDot.Identifiers.Aggregate(
"", (current, id) => current + (GetIdentifierName(
id) + idDot.Separator));
425 genericName = genericName.Substring(0, genericName.Length - idDot.Separator.Length);
428 genericName = identifier.Text;
430 if (genericName == null)
431 throw new Exception(
string.Format(
"Unable to find the name of the generic [{0}]", identifier));
448 foreach (var macro
in macros)
450 newMacros.RemoveAll(x => x.Name == macro.Name);
451 newMacros.Add(macro);
455 foreach (var macro
in mixin.Macros)
457 newMacros.RemoveAll(x => x.Name == macro.Name);
459 newMacros.Add(tempMacro);
462 mixin.Macros = newMacros.Select(x =>
new ShaderMacro(x.Name, x.Definition)).ToList();
463 return newMacros.ToArray();
SiliconStudio.Paradox.Shaders.ShaderMacro ShaderMacro
Macro to be used with PreProcessor.
Search recursively all in and out dependencies.
SiliconStudio.Core.Diagnostics.LoggerResult LoggerResult
A class to collect parsing/expression messages.
A member reference in the form {this}.{Name}
A generic identifier in the form Typename
A reference to a variable.
A hash to uniquely identify data.
List< Identifier > Identifiers
Gets or sets the path.
AnalysisStatus
A status needed to analyze the mixin in the correct order within a compilation module ...