/// <summary> /// Check if a previously analyzed instance of the shader can be used /// </summary> /// <param name="mixinInfo">the ModuleMixinInfo</param> private void CheckMixinForReplacement(ModuleMixinInfo mixinInfo) { // TODO: infinite loop when cross reference (composition & =stage for example) // TODO: change ReplacementChecked to enum None/InProgress/Done if (mixinInfo.ReplacementChecked) { return; } // Check parents and dependencies mixinInfo.MinimalContext.Where(x => x != mixinInfo).ForEach(CheckMixinForReplacement); foreach (var replaceCandidateMixinInfo in MixinInfos.Where(x => x != mixinInfo && x.ShaderSource.Equals(mixinInfo.ShaderSource) && x.HashPreprocessSource == mixinInfo.HashPreprocessSource)) { if (replaceCandidateMixinInfo != null && replaceCandidateMixinInfo.Mixin.DependenciesStatus != AnalysisStatus.None) { if (replaceCandidateMixinInfo.Mixin.MinimalContext != null) { var noNeedToReplaced = replaceCandidateMixinInfo.Mixin.MinimalContext .Where(dep => dep != replaceCandidateMixinInfo.Mixin) .All(dep => mixinInfo.MinimalContext.FirstOrDefault(x => x.Mixin == dep) != null); if (noNeedToReplaced) { mixinInfo.Mixin = replaceCandidateMixinInfo.Mixin; mixinInfo.MixinAst = replaceCandidateMixinInfo.MixinAst; mixinInfo.MixinGenericName = replaceCandidateMixinInfo.MixinGenericName; break; } } } } mixinInfo.ReplacementChecked = true; }
/// <summary> /// Build the virtual table for the specified mixin /// </summary> /// <param name="mixinInfo">the mixin</param> private void BuildVirtualTables(ModuleMixinInfo mixinInfo) { var mixin = mixinInfo.Mixin; if (mixin.VirtualTableStatus == AnalysisStatus.Error || mixin.VirtualTableStatus == AnalysisStatus.Cyclic || mixin.VirtualTableStatus == AnalysisStatus.Complete) { return; } if (!mixinInfo.Instanciated) { return; } foreach (var dep in mixin.InheritanceList) { var depInfo = MixinInfos.FirstOrDefault(x => x.Mixin == dep); BuildVirtualTables(depInfo); } // merge the virtual tables foreach (var dep in mixin.InheritanceList) { mixin.VirtualTable.MergeWithLocalVirtualTable(dep.LocalVirtualTable, mixin.MixinName, ErrorWarningLog); } mixin.VirtualTable.MergeWithLocalVirtualTable(mixin.LocalVirtualTable, mixin.MixinName, ErrorWarningLog); foreach (var dep in mixin.InheritanceList) { mixin.VirtualTable.AddVirtualTable(dep.VirtualTable, dep.MixinName, ErrorWarningLog); } mixin.VirtualTable.AddFinalDeclarations(mixin.LocalVirtualTable.Methods.Select(x => x.Method).ToList(), mixin.MixinName, ErrorWarningLog); foreach (var variable in mixin.VirtualTable.Variables) { // Compare local against local and inherited against inherited var local = variable.Shader == mixin.Shader; if (mixin.VirtualTable.Variables.Any(x => (x.Shader == mixin.Shader) == local && x.Variable != variable.Variable && x.Variable.Name.Text == variable.Variable.Name.Text)) { mixin.PotentialConflictingVariables.Add(variable.Variable); } } foreach (var method in mixin.VirtualTable.Methods) { // Compare local against local and inherited against inherited var local = method.Shader == mixin.Shader; if (mixin.VirtualTable.Methods.Any(x => (x.Shader == mixin.Shader) == local && x.Method != method.Method && x.Method.IsSameSignature(method.Method))) { mixin.PotentialConflictingMethods.Add(method.Method); } } CheckStageClass(mixin); mixin.VirtualTableStatus = AnalysisStatus.Complete; }
public ModuleMixinInfo Copy(SiliconStudio.Shaders.Parser.ShaderMacro[] macros) { var mixinInfo = new ModuleMixinInfo(); mixinInfo.ShaderSource = ShaderSource; mixinInfo.MixinAst = MixinAst; mixinInfo.MixinGenericName = MixinGenericName; mixinInfo.Mixin = Mixin; mixinInfo.Instanciated = Instanciated; mixinInfo.HashPreprocessSource = HashPreprocessSource; mixinInfo.Macros = macros; return(mixinInfo); }
/// <summary> /// Build the ModuleMixinInfo class /// </summary> /// <param name="shaderSource">the ShaderSource to load</param> /// <param name="macros">the macros applied on the source</param> /// <returns>the ModuleMixinInfo</returns> private ModuleMixinInfo BuildMixinInfo(ShaderSource shaderSource, SiliconStudio.Shaders.Parser.ShaderMacro[] macros) { ModuleMixinInfo mixinInfo = null; if (shaderSource is ShaderClassSource) { var shaderClassSource = shaderSource as ShaderClassSource; mixinInfo = new ModuleMixinInfo { ShaderSource = shaderClassSource, Macros = macros }; LoadMixinFromClassSource(mixinInfo); } else if (shaderSource is ShaderMixinSource) { var shaderMixinSource = shaderSource as ShaderMixinSource; var shaderName = "Mix" + lastMixIndex; ++lastMixIndex; var fakeAst = new ShaderClassType(shaderName); foreach (var classSource in shaderMixinSource.Mixins) { Identifier name; if (classSource.GenericArguments != null && classSource.GenericArguments.Length > 0) { name = new IdentifierGeneric(classSource.ClassName, classSource.GenericArguments.Select(x => new Identifier(x.ToString())).ToArray()); } else { name = new Identifier(classSource.ClassName); } fakeAst.BaseClasses.Add(new TypeName(name)); } mixinInfo = new ModuleMixinInfo { MixinGenericName = shaderName, Macros = macros, MixinAst = fakeAst, ShaderSource = shaderSource, SourceHash = ObjectId.FromBytes(Encoding.UTF8.GetBytes(shaderName)), Instanciated = true }; } return(mixinInfo); }
/// <summary> /// Loads the mixin based on its ShaderSource /// </summary> /// <param name="mixinInfo">the ModuleMixinInfo</param> private void LoadMixinFromClassSource(ModuleMixinInfo mixinInfo) { var classSource = (ShaderClassSource)mixinInfo.ShaderSource; // If we allow to parse non instantiated generics, put empty generic arguments to let the ShaderLoader expanding correctly the class var shaderClass = ShaderLoader.LoadClassSource(classSource, mixinInfo.Macros, mixinInfo.Log, AllowNonInstantiatedGenerics); // If result is null, there was some errors while parsing. if (shaderClass == null) { return; } shaderClass = shaderClass.DeepClone(); if (shaderClass.ShaderGenerics.Count > 0) { mixinInfo.Instanciated = false; } mixinInfo.HashPreprocessSource = shaderClass.PreprocessedSourceHash; mixinInfo.SourceHash = shaderClass.SourceHash; if (!SourceHashes.ContainsKey(classSource.ClassName)) { SourceHashes.Add(classSource.ClassName, shaderClass.SourceHash); } // check if it was a generic class and find out if the instanciation was correct if (shaderClass.GenericParameters.Count > 0) { if (classSource.GenericArguments == null || classSource.GenericArguments.Length == 0 || shaderClass.GenericParameters.Count > classSource.GenericArguments.Length) { mixinInfo.Instanciated = false; mixinInfo.Log.Error(XenkoMessageCode.ErrorClassSourceNotInstantiated, shaderClass.Span, classSource.ClassName); } else { ModuleMixinInfo.CleanIdentifiers(shaderClass.GenericParameters.Select(x => x.Name).ToList()); } } mixinInfo.MixinAst = shaderClass; mixinInfo.MixinGenericName = classSource.ClassName; }
/// <summary> /// Loads generic classes that may appear in the mixin /// </summary> /// <param name="mixinInfo">The mixin to investigate</param> /// <param name="macros">The macros.</param> /// <param name="macrosString">The macros string.</param> private void LoadNecessaryShaders(ModuleMixinInfo mixinInfo, SiliconStudio.Shaders.Parser.ShaderMacro[] macros, string macrosString) { if (!mixinInfo.Instanciated) { return; } // Look for all the generic calls var shaderDependencyVisitor = new ShaderDependencyVisitor(mixinInfo.Log, ShaderLoader.SourceManager); shaderDependencyVisitor.Run(mixinInfo.MixinAst); foreach (var foundClass in shaderDependencyVisitor.FoundClasses) { var classSource = new ShaderClassSource(foundClass, null); var foundMixinInfo = GetModuleMixinInfo(classSource, macros, macrosString); mixinInfo.MinimalContext.UnionWith(foundMixinInfo.MinimalContext); } foreach (var id in shaderDependencyVisitor.FoundIdentifiers) { var genericClass = id.Item1; ModuleMixinInfo.CleanIdentifiers(genericClass.Identifiers); var genericParams = BuildShaderGenericParameters(genericClass); var classSource = new ShaderClassSource(genericClass.Text, genericParams); var instanciatedClassInfo = GetModuleMixinInfo(classSource, macros, macrosString); mixinInfo.MinimalContext.UnionWith(instanciatedClassInfo.MinimalContext); var newId = new Identifier(instanciatedClassInfo.MixinName); if (id.Item2 is TypeName) // in the baseclass list or in a variable declaration { (id.Item2 as TypeName).Name = newId; } else if (id.Item2 is VariableReferenceExpression) { (id.Item2 as VariableReferenceExpression).Name = newId; } else if (id.Item2 is MemberReferenceExpression) { (id.Item2 as MemberReferenceExpression).Member = newId; } } }
/// <summary> /// Performs type analysis for each mixin /// </summary> /// <param name="mixinInfo">the ModuleMixinInfo</param> private void PerformTypeAnalysis(ModuleMixinInfo mixinInfo) { lock (mixinInfo) { if (mixinInfo.Mixin.TypeAnalysisStatus == AnalysisStatus.None) { mixinInfo.Mixin.TypeAnalysisStatus = AnalysisStatus.InProgress; // TODO: order + typedef var typeAnalyzer = new XenkoTypeAnalysis(new ParsingResult()); typeAnalyzer.Run(mixinInfo.MixinAst); mixinInfo.Mixin.TypeAnalysisStatus = AnalysisStatus.Complete; } else if (mixinInfo.Mixin.TypeAnalysisStatus != AnalysisStatus.Complete) { throw new Exception("Type analysis failed for mixin " + mixinInfo.MixinName); } } }
/// <summary> /// Performs an semantic analysis of the mixin inside its own context /// </summary> /// <param name="mixinInfo">The mixin to analyze</param> private void ModuleSemanticAnalysisPerMixin(ModuleMixinInfo mixinInfo) { if (mixinInfo == null) return; var mixin = mixinInfo.Mixin; if (mixin.DependenciesStatus != AnalysisStatus.Complete || mixin.VirtualTableStatus != AnalysisStatus.Complete) return; if (mixin.SemanticAnalysisStatus == AnalysisStatus.Complete) return; if (mixin.SemanticAnalysisStatus == AnalysisStatus.InProgress) { ErrorWarningLog.Error(XenkoMessageCode.ErrorCyclicDependency, mixin.Shader.Span, mixin.Shader); return; } if (!mixinInfo.Instanciated) return; mixin.SemanticAnalysisStatus = AnalysisStatus.InProgress; // analyze the base mixins foreach (var baseClass in mixin.BaseMixins) { var baseClassInfo = MixinInfos.FirstOrDefault(x => x.Mixin == baseClass); ModuleSemanticAnalysisPerMixin(baseClassInfo); if (baseClassInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Error || baseClassInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Cyclic) { mixin.SemanticAnalysisStatus = AnalysisStatus.Error; return; } if (!baseClassInfo.Instanciated) { mixinInfo.Instanciated = false; mixin.SemanticAnalysisStatus = AnalysisStatus.None; return; } if (mixin.LocalVirtualTable.CheckNameConflict(baseClass.VirtualTable, ErrorWarningLog)) { mixin.SemanticAnalysisStatus = AnalysisStatus.Error; return; } } // analyze the extern mixins foreach (var externMixin in mixin.VariableDependencies) { var externMixinInfo = MixinInfos.FirstOrDefault(x => x.Mixin == externMixin.Value); ModuleSemanticAnalysisPerMixin(externMixinInfo); if (externMixinInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Error || externMixinInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Cyclic) { mixin.SemanticAnalysisStatus = AnalysisStatus.Error; return; } } var compilationContext = MixinInfos.Select(x => x.Mixin).ToList(); mixin.ParsingInfo = XenkoSemanticAnalysis.RunAnalysis(mixin, compilationContext); var staticStageMixins = new List<ModuleMixin>(); staticStageMixins.AddRange(mixin.ParsingInfo.StaticReferences.VariablesReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin)); staticStageMixins.AddRange(mixin.ParsingInfo.StaticReferences.MethodsReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin)); staticStageMixins.AddRange(mixin.ParsingInfo.StageInitReferences.VariablesReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin)); staticStageMixins.AddRange(mixin.ParsingInfo.StageInitReferences.MethodsReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin)); staticStageMixins.RemoveAll(x => x == mixin); foreach (var dep in staticStageMixins) { var depInfo = MixinInfos.FirstOrDefault(x => x.Mixin == dep); ModuleSemanticAnalysisPerMixin(depInfo); if (dep.SemanticAnalysisStatus == AnalysisStatus.Error || dep.SemanticAnalysisStatus == AnalysisStatus.Cyclic) { mixin.SemanticAnalysisStatus = AnalysisStatus.Error; return; } } // check the extern stage references (but do not change the type inference) var externList = new List<ModuleMixin>(); // NOTE: we cannot use the members .Values of .Keys because it internally modifies the dictionary, creating a Dictionary<K,T>.ValueCollection which cannot be cloned (no default constructor). // This results in a exception in the DeepClone code. mixin.InheritanceList.ForEach(dep => externList.AddRange(dep.VariableDependencies.Select(x => x.Value))); externList.AddRange(mixin.VariableDependencies.Select(x => x.Value)); externList.ForEach(ext => CheckReferencesFromExternMixin(ext, mixin)); mixin.ParsingInfo.ErrorsWarnings.CopyTo(ErrorWarningLog); mixin.SemanticAnalysisStatus = AnalysisStatus.Complete; }
/// <summary> /// Specifically analyze a module /// </summary> /// <param name="mixinInfo">the ModuleMixinInfo</param> public void Analyze(ModuleMixinInfo mixinInfo) { ModuleSemanticAnalysisPerMixin(mixinInfo); }
/// <summary> /// Performs an semantic analysis of the mixin inside its own context /// </summary> /// <param name="mixinInfo">The mixin to analyze</param> private void ModuleSemanticAnalysisPerMixin(ModuleMixinInfo mixinInfo) { if (mixinInfo == null) { return; } var mixin = mixinInfo.Mixin; if (mixin.DependenciesStatus != AnalysisStatus.Complete || mixin.VirtualTableStatus != AnalysisStatus.Complete) { return; } if (mixin.SemanticAnalysisStatus == AnalysisStatus.Complete) { return; } if (mixin.SemanticAnalysisStatus == AnalysisStatus.InProgress) { ErrorWarningLog.Error(XenkoMessageCode.ErrorCyclicDependency, mixin.Shader.Span, mixin.Shader); return; } if (!mixinInfo.Instanciated) { return; } mixin.SemanticAnalysisStatus = AnalysisStatus.InProgress; // analyze the base mixins foreach (var baseClass in mixin.BaseMixins) { var baseClassInfo = MixinInfos.FirstOrDefault(x => x.Mixin == baseClass); ModuleSemanticAnalysisPerMixin(baseClassInfo); if (baseClassInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Error || baseClassInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Cyclic) { mixin.SemanticAnalysisStatus = AnalysisStatus.Error; return; } if (!baseClassInfo.Instanciated) { mixinInfo.Instanciated = false; mixin.SemanticAnalysisStatus = AnalysisStatus.None; return; } if (mixin.LocalVirtualTable.CheckNameConflict(baseClass.VirtualTable, ErrorWarningLog)) { mixin.SemanticAnalysisStatus = AnalysisStatus.Error; return; } } // analyze the extern mixins foreach (var externMixin in mixin.VariableDependencies) { var externMixinInfo = MixinInfos.FirstOrDefault(x => x.Mixin == externMixin.Value); ModuleSemanticAnalysisPerMixin(externMixinInfo); if (externMixinInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Error || externMixinInfo.Mixin.SemanticAnalysisStatus == AnalysisStatus.Cyclic) { mixin.SemanticAnalysisStatus = AnalysisStatus.Error; return; } } var compilationContext = MixinInfos.Select(x => x.Mixin).ToList(); mixin.ParsingInfo = XenkoSemanticAnalysis.RunAnalysis(mixin, compilationContext); var staticStageMixins = new List <ModuleMixin>(); staticStageMixins.AddRange(mixin.ParsingInfo.StaticReferences.VariablesReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin)); staticStageMixins.AddRange(mixin.ParsingInfo.StaticReferences.MethodsReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin)); staticStageMixins.AddRange(mixin.ParsingInfo.StageInitReferences.VariablesReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin)); staticStageMixins.AddRange(mixin.ParsingInfo.StageInitReferences.MethodsReferences.Select(x => x.Key.GetTag(XenkoTags.ShaderScope) as ModuleMixin)); staticStageMixins.RemoveAll(x => x == mixin); foreach (var dep in staticStageMixins) { var depInfo = MixinInfos.FirstOrDefault(x => x.Mixin == dep); ModuleSemanticAnalysisPerMixin(depInfo); if (dep.SemanticAnalysisStatus == AnalysisStatus.Error || dep.SemanticAnalysisStatus == AnalysisStatus.Cyclic) { mixin.SemanticAnalysisStatus = AnalysisStatus.Error; return; } } // check the extern stage references (but do not change the type inference) var externList = new List <ModuleMixin>(); // NOTE: we cannot use the members .Values of .Keys because it internally modifies the dictionary, creating a Dictionary<K,T>.ValueCollection which cannot be cloned (no default constructor). // This results in a exception in the DeepClone code. mixin.InheritanceList.ForEach(dep => externList.AddRange(dep.VariableDependencies.Select(x => x.Value))); externList.AddRange(mixin.VariableDependencies.Select(x => x.Value)); externList.ForEach(ext => CheckReferencesFromExternMixin(ext, mixin)); mixin.ParsingInfo.ErrorsWarnings.CopyTo(ErrorWarningLog); mixin.SemanticAnalysisStatus = AnalysisStatus.Complete; }
/// <summary> /// Loads the mixin based on its ShaderSource /// </summary> /// <param name="mixinInfo">the ModuleMixinInfo</param> private void LoadMixinFromClassSource(ModuleMixinInfo mixinInfo) { var classSource = (ShaderClassSource)mixinInfo.ShaderSource; // If we allow to parse non instantiated generics, put empty generic arguments to let the ShaderLoader expanding correctly the class var shaderClass = ShaderLoader.LoadClassSource(classSource, mixinInfo.Macros, mixinInfo.Log, AllowNonInstantiatedGenerics); // If result is null, there was some errors while parsing. if (shaderClass == null) return; shaderClass = shaderClass.DeepClone(); if (shaderClass.ShaderGenerics.Count > 0) mixinInfo.Instanciated = false; mixinInfo.HashPreprocessSource = shaderClass.PreprocessedSourceHash; mixinInfo.SourceHash = shaderClass.SourceHash; if (!SourceHashes.ContainsKey(classSource.ClassName)) SourceHashes.Add(classSource.ClassName, shaderClass.SourceHash); // check if it was a generic class and find out if the instanciation was correct if (shaderClass.GenericParameters.Count > 0) { if (classSource.GenericArguments == null || classSource.GenericArguments.Length == 0 || shaderClass.GenericParameters.Count > classSource.GenericArguments.Length) { mixinInfo.Instanciated = false; mixinInfo.Log.Error(XenkoMessageCode.ErrorClassSourceNotInstantiated, shaderClass.Span, classSource.ClassName); } else { ModuleMixinInfo.CleanIdentifiers(shaderClass.GenericParameters.Select(x => x.Name).ToList()); } } mixinInfo.MixinAst = shaderClass; mixinInfo.MixinGenericName = classSource.ClassName; }
/// <summary> /// Get the list of dependencies for the mixin (base classes only) /// </summary> /// <param name="mixinInfo">the mixin info</param> /// <returns>A collection of class names</returns> private void BuildMixinDependencies(ModuleMixinInfo mixinInfo) { var mixin = mixinInfo.Mixin; if (mixin.DependenciesStatus == AnalysisStatus.Cyclic || mixin.DependenciesStatus == AnalysisStatus.Error || mixinInfo.Mixin.DependenciesStatus == AnalysisStatus.Complete) { return; } if (mixin.DependenciesStatus == AnalysisStatus.InProgress) { ErrorWarningLog.Error(XenkoMessageCode.ErrorCyclicDependency, mixin.Shader.Span, mixin.Shader); mixin.DependenciesStatus = AnalysisStatus.Cyclic; return; } if (mixin.DependenciesStatus == AnalysisStatus.None) { mixin.DependenciesStatus = AnalysisStatus.InProgress; foreach (var baseClass in mixin.Shader.BaseClasses) { // search based on class name and macros. It is enough since a ShaderMixinSource only have ShaderClassSource as base mixin (and no ShaderMixinSource that may redefine the macros) var bcInfo = MixinInfos.FirstOrDefault(x => x.MixinName == baseClass.Name.Text && AreMacrosEqual(x.Macros, mixinInfo.Macros)); if (bcInfo == null) { ErrorWarningLog.Error(XenkoMessageCode.ErrorDependencyNotInModule, baseClass.Span, baseClass, mixin.MixinName); mixin.DependenciesStatus = AnalysisStatus.Error; return; } var bc = bcInfo.Mixin; BuildMixinDependencies(bcInfo); if (bc.DependenciesStatus == AnalysisStatus.Error || bc.DependenciesStatus == AnalysisStatus.Cyclic) { mixin.DependenciesStatus = AnalysisStatus.Error; return; } foreach (var dependency in bc.InheritanceList) { if (!mixin.InheritanceList.Contains(dependency)) { mixin.InheritanceList.Add(dependency); } } if (!mixin.InheritanceList.Contains(bc)) { mixin.InheritanceList.Add(bc); } mixin.BaseMixins.Add(bc); if (!bcInfo.Instanciated) { mixinInfo.Instanciated = false; } } // do not look for extern keyword but for type name foreach (var variable in mixin.LocalVirtualTable.Variables) { var variableTypeName = variable.Variable.Type.Name.Text; if (variable.Variable.Type is ArrayType) // support for array of externs { variableTypeName = (variable.Variable.Type as ArrayType).Type.Name.Text; } var baseClassInfo = MixinInfos.FirstOrDefault(x => x.MixinName == variableTypeName); if (baseClassInfo != null) { variable.Variable.Qualifiers |= SiliconStudio.Shaders.Ast.Hlsl.StorageQualifier.Extern; // add the extern keyword but simpler analysis in the future if (variable.Variable.InitialValue is VariableReferenceExpression && (variable.Variable.InitialValue as VariableReferenceExpression).Name.Text == "stage") { mixin.StageInitVariableDependencies.Add(variable.Variable, baseClassInfo.Mixin); } else { mixin.VariableDependencies.Add(variable.Variable, baseClassInfo.Mixin); } if (variable.Variable.Type is ArrayType) { var typeArray = variable.Variable.Type as ArrayType; typeArray.Type.TypeInference.Declaration = baseClassInfo.MixinAst; } else { variable.Variable.Type.TypeInference.Declaration = baseClassInfo.MixinAst; } } } mixin.DependenciesStatus = AnalysisStatus.Complete; } }
/// <summary> /// Build the ModuleMixinInfo class /// </summary> /// <param name="shaderSource">the ShaderSource to load</param> /// <param name="macros">the macros applied on the source</param> /// <returns>the ModuleMixinInfo</returns> private ModuleMixinInfo BuildMixinInfo(ShaderSource shaderSource, SiliconStudio.Shaders.Parser.ShaderMacro[] macros) { ModuleMixinInfo mixinInfo = null; if (shaderSource is ShaderClassSource) { var shaderClassSource = shaderSource as ShaderClassSource; mixinInfo = new ModuleMixinInfo { ShaderSource = shaderClassSource, Macros = macros }; LoadMixinFromClassSource(mixinInfo); } else if (shaderSource is ShaderMixinSource) { var shaderMixinSource = shaderSource as ShaderMixinSource; var shaderName = "Mix" + lastMixIndex; ++lastMixIndex; var fakeAst = new ShaderClassType(shaderName); foreach (var classSource in shaderMixinSource.Mixins) { Identifier name; if (classSource.GenericArguments != null && classSource.GenericArguments.Length > 0) name = new IdentifierGeneric(classSource.ClassName, classSource.GenericArguments.Select(x => new Identifier(x.ToString())).ToArray()); else name = new Identifier(classSource.ClassName); fakeAst.BaseClasses.Add(new TypeName(name)); } mixinInfo = new ModuleMixinInfo { MixinGenericName = shaderName, Macros = macros, MixinAst = fakeAst, ShaderSource = shaderSource, SourceHash = ObjectId.FromBytes(Encoding.UTF8.GetBytes(shaderName)), Instanciated = true }; } return mixinInfo; }
/// <summary> /// Loads generic classes that may appear in the mixin /// </summary> /// <param name="mixinInfo">The mixin to investigate</param> /// <param name="macros">The macros.</param> /// <param name="macrosString">The macros string.</param> private void LoadNecessaryShaders(ModuleMixinInfo mixinInfo, SiliconStudio.Shaders.Parser.ShaderMacro[] macros, string macrosString) { if (!mixinInfo.Instanciated) return; // Look for all the generic calls var shaderDependencyVisitor = new ShaderDependencyVisitor(mixinInfo.Log, ShaderLoader.SourceManager); shaderDependencyVisitor.Run(mixinInfo.MixinAst); foreach (var foundClass in shaderDependencyVisitor.FoundClasses) { var classSource = new ShaderClassSource(foundClass, null); var foundMixinInfo = GetModuleMixinInfo(classSource, macros, macrosString); mixinInfo.MinimalContext.UnionWith(foundMixinInfo.MinimalContext); } foreach (var id in shaderDependencyVisitor.FoundIdentifiers) { var genericClass = id.Item1; ModuleMixinInfo.CleanIdentifiers(genericClass.Identifiers); var genericParams = BuildShaderGenericParameters(genericClass); var classSource = new ShaderClassSource(genericClass.Text, genericParams); var instanciatedClassInfo = GetModuleMixinInfo(classSource, macros, macrosString); mixinInfo.MinimalContext.UnionWith(instanciatedClassInfo.MinimalContext); var newId = new Identifier(instanciatedClassInfo.MixinName); if (id.Item2 is TypeName) // in the baseclass list or in a variable declaration (id.Item2 as TypeName).Name = newId; else if (id.Item2 is VariableReferenceExpression) (id.Item2 as VariableReferenceExpression).Name = newId; else if (id.Item2 is MemberReferenceExpression) (id.Item2 as MemberReferenceExpression).Member = newId; } }
/// <summary> /// Build the virtual table for the specified mixin /// </summary> /// <param name="mixinInfo">the mixin</param> private void BuildVirtualTables(ModuleMixinInfo mixinInfo) { var mixin = mixinInfo.Mixin; if (mixin.VirtualTableStatus == AnalysisStatus.Error || mixin.VirtualTableStatus == AnalysisStatus.Cyclic || mixin.VirtualTableStatus == AnalysisStatus.Complete) return; if (!mixinInfo.Instanciated) return; foreach (var dep in mixin.InheritanceList) { var depInfo = MixinInfos.FirstOrDefault(x => x.Mixin == dep); BuildVirtualTables(depInfo); } // merge the virtual tables foreach (var dep in mixin.InheritanceList) mixin.VirtualTable.MergeWithLocalVirtualTable(dep.LocalVirtualTable, mixin.MixinName, ErrorWarningLog); mixin.VirtualTable.MergeWithLocalVirtualTable(mixin.LocalVirtualTable, mixin.MixinName, ErrorWarningLog); foreach (var dep in mixin.InheritanceList) mixin.VirtualTable.AddVirtualTable(dep.VirtualTable, dep.MixinName, ErrorWarningLog); mixin.VirtualTable.AddFinalDeclarations(mixin.LocalVirtualTable.Methods.Select(x => x.Method).ToList(), mixin.MixinName, ErrorWarningLog); foreach (var variable in mixin.VirtualTable.Variables.Select(x => x.Variable)) { if (mixin.VirtualTable.Variables.Any(x => x.Variable != variable && x.Variable.Name.Text == variable.Name.Text)) mixin.PotentialConflictingVariables.Add(variable); } foreach (var method in mixin.VirtualTable.Methods.Select(x => x.Method)) { if (mixin.VirtualTable.Methods.Any(x => x.Method != method && x.Method.IsSameSignature(method))) mixin.PotentialConflictingMethods.Add(method); } CheckStageClass(mixin); mixin.VirtualTableStatus = AnalysisStatus.Complete; }
/// <summary> /// Get the list of dependencies for the mixin (base classes only) /// </summary> /// <param name="mixinInfo">the mixin info</param> /// <returns>A collection of class names</returns> private void BuildMixinDependencies(ModuleMixinInfo mixinInfo) { var mixin = mixinInfo.Mixin; if (mixin.DependenciesStatus == AnalysisStatus.Cyclic || mixin.DependenciesStatus == AnalysisStatus.Error || mixinInfo.Mixin.DependenciesStatus == AnalysisStatus.Complete) return; if (mixin.DependenciesStatus == AnalysisStatus.InProgress) { ErrorWarningLog.Error(XenkoMessageCode.ErrorCyclicDependency, mixin.Shader.Span, mixin.Shader); mixin.DependenciesStatus = AnalysisStatus.Cyclic; return; } if (mixin.DependenciesStatus == AnalysisStatus.None) { mixin.DependenciesStatus = AnalysisStatus.InProgress; foreach (var baseClass in mixin.Shader.BaseClasses) { // search based on class name and macros. It is enough since a ShaderMixinSource only have ShaderClassSource as base mixin (and no ShaderMixinSource that may redefine the macros) var bcInfo = MixinInfos.FirstOrDefault(x => x.MixinName == baseClass.Name.Text && AreMacrosEqual(x.Macros, mixinInfo.Macros)); if (bcInfo == null) { ErrorWarningLog.Error(XenkoMessageCode.ErrorDependencyNotInModule, baseClass.Span, baseClass, mixin.MixinName); mixin.DependenciesStatus = AnalysisStatus.Error; return; } var bc = bcInfo.Mixin; BuildMixinDependencies(bcInfo); if (bc.DependenciesStatus == AnalysisStatus.Error || bc.DependenciesStatus == AnalysisStatus.Cyclic) { mixin.DependenciesStatus = AnalysisStatus.Error; return; } foreach (var dependency in bc.InheritanceList) { if (!mixin.InheritanceList.Contains(dependency)) mixin.InheritanceList.Add(dependency); } if (!mixin.InheritanceList.Contains(bc)) mixin.InheritanceList.Add(bc); mixin.BaseMixins.Add(bc); if (!bcInfo.Instanciated) mixinInfo.Instanciated = false; } // do not look for extern keyword but for type name foreach (var variable in mixin.LocalVirtualTable.Variables) { var variableTypeName = variable.Variable.Type.Name.Text; if (variable.Variable.Type is ArrayType) // support for array of externs variableTypeName = (variable.Variable.Type as ArrayType).Type.Name.Text; var baseClassInfo = MixinInfos.FirstOrDefault(x => x.MixinName == variableTypeName); if (baseClassInfo != null) { variable.Variable.Qualifiers |= SiliconStudio.Shaders.Ast.Hlsl.StorageQualifier.Extern; // add the extern keyword but simpler analysis in the future if (variable.Variable.InitialValue is VariableReferenceExpression && (variable.Variable.InitialValue as VariableReferenceExpression).Name.Text == "stage") mixin.StageInitVariableDependencies.Add(variable.Variable, baseClassInfo.Mixin); else mixin.VariableDependencies.Add(variable.Variable, baseClassInfo.Mixin); if (variable.Variable.Type is ArrayType) { var typeArray = variable.Variable.Type as ArrayType; typeArray.Type.TypeInference.Declaration = baseClassInfo.MixinAst; } else { variable.Variable.Type.TypeInference.Declaration = baseClassInfo.MixinAst; } } } mixin.DependenciesStatus = AnalysisStatus.Complete; } }
/// <summary> /// Get all the declarations in the mixin /// </summary> /// <param name="mixinInfo">The mixin info</param> private void BuildModuleMixin(ModuleMixinInfo mixinInfo) { lock (mixinInfo) { if (mixinInfo.Mixin.ModuleMixinBuildStatus == AnalysisStatus.Complete) return; if (mixinInfo.Mixin.ModuleMixinBuildStatus != AnalysisStatus.None) throw new Exception("BuildModuleMixin failed for mixin " + mixinInfo.MixinName); mixinInfo.Mixin.ModuleMixinBuildStatus = AnalysisStatus.InProgress; var mixinAst = mixinInfo.MixinAst; var moduleMixin = mixinInfo.Mixin; moduleMixin.SetShaderAst(mixinAst); moduleMixin.MixinGenericName = mixinInfo.MixinGenericName; if (mixinAst != null && mixinInfo.Instanciated) { var vtindex = 0; foreach (var member in mixinAst.Members) { if (member is MethodDeclaration) { moduleMixin.LocalVirtualTable.Methods.Add(new MethodDeclarationShaderCouple(member as MethodDeclaration, mixinAst)); member.SetTag(XenkoTags.VirtualTableReference, new VTableReference { Shader = mixinInfo.MixinName, Slot = vtindex++ }); } else if (member is Variable) { var variable = member as Variable; moduleMixin.LocalVirtualTable.Variables.Add(new VariableShaderCouple(variable, mixinAst)); // remove null initial values var initValue = variable.InitialValue as VariableReferenceExpression; if (initValue != null && initValue.Name.Text == "null") variable.InitialValue = null; } else if (member is Typedef) moduleMixin.LocalVirtualTable.Typedefs.Add(member as Typedef); else if (member is StructType) moduleMixin.LocalVirtualTable.StructureTypes.Add(member as StructType); else moduleMixin.RemainingNodes.Add(member); // set a tag on the members to easily recognize them from the local declarations/definitions member.SetTag(XenkoTags.ShaderScope, moduleMixin); member.SetTag(XenkoTags.BaseDeclarationMixin, mixinInfo.MixinName); } // Check name conflicts foreach (var method in moduleMixin.LocalVirtualTable.Methods) { if (moduleMixin.LocalVirtualTable.Methods.Count(x => x.Method.IsSameSignature(method.Method)) > 1) // 1 because the function is in the list ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionRedefined, method.Method.Span, method.Method, mixinInfo.MixinName); if (moduleMixin.LocalVirtualTable.Variables.Any(x => x.Variable.Name.Text == method.Method.Name.Text)) ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionVariableNameConflict, method.Method.Span, method.Method, mixinInfo.MixinName); } foreach (var variable in moduleMixin.LocalVirtualTable.Variables) { if (moduleMixin.LocalVirtualTable.Variables.Count(x => x.Variable.Name.Text == variable.Variable.Name.Text) > 1) // 1 because the function is in the list ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionVariableNameConflict, variable.Variable.Span, variable.Variable, mixinInfo.MixinName); if (moduleMixin.LocalVirtualTable.Methods.Any(x => x.Method.Name.Text == variable.Variable.Name.Text)) ErrorWarningLog.Error(XenkoMessageCode.ErrorVariableNameConflict, variable.Variable.Span, variable.Variable, mixinInfo.MixinName); } } moduleMixin.MinimalContext = new HashSet<ModuleMixin>(mixinInfo.MinimalContext.Select(x => x.Mixin)); mixinInfo.Mixin.ModuleMixinBuildStatus = AnalysisStatus.Complete; } }
/// <summary> /// Get all the declarations in the mixin /// </summary> /// <param name="mixinInfo">The mixin info</param> private void BuildModuleMixin(ModuleMixinInfo mixinInfo) { lock (mixinInfo) { if (mixinInfo.Mixin.ModuleMixinBuildStatus == AnalysisStatus.Complete) { return; } if (mixinInfo.Mixin.ModuleMixinBuildStatus != AnalysisStatus.None) { throw new Exception("BuildModuleMixin failed for mixin " + mixinInfo.MixinName); } mixinInfo.Mixin.ModuleMixinBuildStatus = AnalysisStatus.InProgress; var mixinAst = mixinInfo.MixinAst; var moduleMixin = mixinInfo.Mixin; moduleMixin.SetShaderAst(mixinAst); moduleMixin.MixinGenericName = mixinInfo.MixinGenericName; if (mixinAst != null && mixinInfo.Instanciated) { var vtindex = 0; foreach (var member in mixinAst.Members) { if (member is MethodDeclaration) { moduleMixin.LocalVirtualTable.Methods.Add(new MethodDeclarationShaderCouple(member as MethodDeclaration, mixinAst)); member.SetTag(XenkoTags.VirtualTableReference, new VTableReference { Shader = mixinInfo.MixinName, Slot = vtindex++ }); } else if (member is Variable) { var variable = member as Variable; moduleMixin.LocalVirtualTable.Variables.Add(new VariableShaderCouple(variable, mixinAst)); // remove null initial values var initValue = variable.InitialValue as VariableReferenceExpression; if (initValue != null && initValue.Name.Text == "null") { variable.InitialValue = null; } } else if (member is Typedef) { moduleMixin.LocalVirtualTable.Typedefs.Add(member as Typedef); } else if (member is StructType) { moduleMixin.LocalVirtualTable.StructureTypes.Add(member as StructType); } else { moduleMixin.RemainingNodes.Add(member); } // set a tag on the members to easily recognize them from the local declarations/definitions member.SetTag(XenkoTags.ShaderScope, moduleMixin); member.SetTag(XenkoTags.BaseDeclarationMixin, mixinInfo.MixinName); } // Check name conflicts foreach (var method in moduleMixin.LocalVirtualTable.Methods) { if (moduleMixin.LocalVirtualTable.Methods.Count(x => x.Method.IsSameSignature(method.Method)) > 1) // 1 because the function is in the list { ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionRedefined, method.Method.Span, method.Method, mixinInfo.MixinName); } if (moduleMixin.LocalVirtualTable.Variables.Any(x => x.Variable.Name.Text == method.Method.Name.Text)) { ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionVariableNameConflict, method.Method.Span, method.Method, mixinInfo.MixinName); } } foreach (var variable in moduleMixin.LocalVirtualTable.Variables) { if (moduleMixin.LocalVirtualTable.Variables.Count(x => x.Variable.Name.Text == variable.Variable.Name.Text) > 1) // 1 because the function is in the list { ErrorWarningLog.Error(XenkoMessageCode.ErrorFunctionVariableNameConflict, variable.Variable.Span, variable.Variable, mixinInfo.MixinName); } if (moduleMixin.LocalVirtualTable.Methods.Any(x => x.Method.Name.Text == variable.Variable.Name.Text)) { ErrorWarningLog.Error(XenkoMessageCode.ErrorVariableNameConflict, variable.Variable.Span, variable.Variable, mixinInfo.MixinName); } } } moduleMixin.MinimalContext = new HashSet <ModuleMixin>(mixinInfo.MinimalContext.Select(x => x.Mixin)); mixinInfo.Mixin.ModuleMixinBuildStatus = AnalysisStatus.Complete; } }
public ModuleMixinInfo Copy(SiliconStudio.Shaders.Parser.ShaderMacro[] macros) { var mixinInfo = new ModuleMixinInfo(); mixinInfo.ShaderSource = ShaderSource; mixinInfo.MixinAst = MixinAst; mixinInfo.MixinGenericName = MixinGenericName; mixinInfo.Mixin = Mixin; mixinInfo.Instanciated = Instanciated; mixinInfo.HashPreprocessSource = HashPreprocessSource; mixinInfo.Macros = macros; return mixinInfo; }
/// <summary> /// Check if a previously analyzed instance of the shader can be used /// </summary> /// <param name="mixinInfo">the ModuleMixinInfo</param> private void CheckMixinForReplacement(ModuleMixinInfo mixinInfo) { // TODO: infinite loop when cross reference (composition & =stage for example) // TODO: change ReplacementChecked to enum None/InProgress/Done if (mixinInfo.ReplacementChecked) return; // Check parents and dependencies mixinInfo.MinimalContext.Where(x => x != mixinInfo).ForEach(CheckMixinForReplacement); foreach (var replaceCandidateMixinInfo in MixinInfos.Where(x => x != mixinInfo && x.ShaderSource.Equals(mixinInfo.ShaderSource) && x.HashPreprocessSource == mixinInfo.HashPreprocessSource)) { if (replaceCandidateMixinInfo.Mixin.DependenciesStatus != AnalysisStatus.None) { if (replaceCandidateMixinInfo.Mixin.MinimalContext != null) { var noNeedToReplaced = replaceCandidateMixinInfo.Mixin.MinimalContext .Where(dep => dep != replaceCandidateMixinInfo.Mixin) .All(dep => mixinInfo.MinimalContext.FirstOrDefault(x => x.Mixin == dep) != null); if (noNeedToReplaced) { mixinInfo.Mixin = replaceCandidateMixinInfo.Mixin; mixinInfo.MixinAst = replaceCandidateMixinInfo.MixinAst; mixinInfo.MixinGenericName = replaceCandidateMixinInfo.MixinGenericName; break; } } } } mixinInfo.ReplacementChecked = true; }