/// <summary> /// Replace the method occurence with its last definition /// </summary> /// <param name="methodDeclaration">the overriding method</param> /// <param name="errorLogger"></param> public void ReplaceVirtualMethod(MethodDeclaration methodDeclaration, LoggerResult errorLogger) { var baseDeclarationMixin = (string)methodDeclaration.GetTag(XenkoTags.BaseDeclarationMixin); foreach (var dict in VirtualTableGroup.Select(x => x.Value)) { for (int i = 0; i < dict.Length; ++i) { var method = dict[i]; var originalDecl = (string)method.GetTag(XenkoTags.BaseDeclarationMixin); // TODO: take typedefs into account... if (originalDecl == baseDeclarationMixin && method.IsSameSignature(methodDeclaration)) { if (method.Qualifiers.Contains(XenkoStorageQualifier.Stage) && !methodDeclaration.Qualifiers.Contains(XenkoStorageQualifier.Stage)) { errorLogger.Warning(XenkoMessageCode.WarningMissingStageKeyword, methodDeclaration.Span, methodDeclaration, (methodDeclaration.GetTag(XenkoTags.ShaderScope) as ModuleMixin).MixinName); methodDeclaration.Qualifiers |= XenkoStorageQualifier.Stage; } else if (!method.Qualifiers.Contains(XenkoStorageQualifier.Stage) && methodDeclaration.Qualifiers.Contains(XenkoStorageQualifier.Stage)) { errorLogger.Error(XenkoMessageCode.ErrorExtraStageKeyword, methodDeclaration.Span, methodDeclaration, method, (methodDeclaration.GetTag(XenkoTags.ShaderScope) as ModuleMixin).MixinName); methodDeclaration.Qualifiers.Values.Remove(XenkoStorageQualifier.Stage); } dict[i] = methodDeclaration; } } } }
/// <summary> /// Merge with a local virtual table = need to check override keywords /// </summary> /// <param name="virtualTable">the virtual table to add</param> /// <param name="mixinName">the name of the mixin</param> /// <param name="log">the error logger</param> public void MergeWithLocalVirtualTable(MixinVirtualTable virtualTable, string mixinName, LoggerResult log) { foreach (var method in virtualTable.Methods) { var methodDecl = Methods.LastOrDefault(x => x.Method.IsSameSignature(method.Method)); if (methodDecl != null) { var isBaseMethod = method.Shader.BaseClasses.Any(x => x.Name.Text == methodDecl.Shader.Name.Text); if (isBaseMethod) { if (methodDecl.Method is MethodDefinition) { if (!method.Method.Qualifiers.Contains(ParadoxStorageQualifier.Override)) { log.Error(ParadoxMessageCode.ErrorMissingOverride, method.Method.Span, method.Method, mixinName); continue; } } else if (method.Method.Qualifiers.Contains(ParadoxStorageQualifier.Override)) { log.Error(ParadoxMessageCode.ErrorOverrideDeclaration, method.Method.Span, method.Method, mixinName); continue; } } Methods.Remove(methodDecl); } else { if (method.Method.Qualifiers.Contains(ParadoxStorageQualifier.Override)) { log.Error(ParadoxMessageCode.ErrorNoMethodToOverride, method.Method.Span, method.Method, mixinName); continue; } } Methods.Add(method); // TODO: handle declarations vs definitions } Variables.UnionWith(virtualTable.Variables.Where(x => !Variables.Contains(x))); StructureTypes.AddRange(virtualTable.StructureTypes.Where(x => !StructureTypes.Contains(x))); Typedefs.AddRange(virtualTable.Typedefs.Where(x => !Typedefs.Contains(x))); }
/// <summary> /// Link all the stage compositions in case it is referenced at several places. /// </summary> /// <param name="variable">The variable of the composition.</param> /// <param name="composition">The composition.</param> /// <param name="dictionary">The already registered compositions.</param> /// <param name="extraDictionary">The new compositions.</param> /// <param name="log">The logger.</param> private static void FullLinkStageCompositions(Variable variable, List<ModuleMixin> composition, CompositionDictionary dictionary, Dictionary<Variable, List<ModuleMixin>> extraDictionary, LoggerResult log) { var mixin = variable.GetTag(ParadoxTags.ShaderScope) as ModuleMixin; if (mixin != null) { var className = mixin.MixinName; foreach (var item in dictionary) { if (item.Key == variable) continue; foreach (var module in item.Value) { if (module.MixinName == className || module.InheritanceList.Any(x => x.MixinName == className)) { // add reference var foundVars = module.FindAllVariablesByName(variable.Name).Where(value => value.Variable.Qualifiers.Contains(ParadoxStorageQualifier.Compose)).ToList();; if (foundVars.Count > 1) { log.Error(ParadoxMessageCode.ErrorAmbiguousComposition, new SourceSpan(), variable.Name); } else if (foundVars.Count > 0) { // if there is already a filled composition, it means that the ShaderMixinSource filled the composition information at two different places // TODO: verify that var foundVar = foundVars[0].Variable; List<ModuleMixin> previousList; if (dictionary.TryGetValue(foundVar, out previousList)) { previousList.AddRange(composition); } else extraDictionary.Add(foundVars[0].Variable, composition); } else { // No matching variable was found // TODO: log a message? } } } } } }
/// <summary> /// create the context for each composition by cloning their dependencies /// </summary> /// <param name="shaderSource">the entry ShaderSource (root)</param> /// <param name="dictionary">the ouputed compositions</param> /// <param name="compilationContext">the compilation context</param> /// <param name="cloneContext">The clone context.</param> /// <returns>a list of all the needed mixins</returns> private static List<ModuleMixin> BuildCompositionsDictionary(ShaderSource shaderSource, CompositionDictionary dictionary, ShaderCompilationContext compilationContext, CloneContext cloneContext, LoggerResult log) { if (shaderSource is ShaderMixinSource) { var shaderMixinSource = shaderSource as ShaderMixinSource; var finalModule = compilationContext.GetModuleMixinFromShaderSource(shaderSource); //PerformanceLogger.Start(PerformanceStage.DeepClone); finalModule = finalModule.DeepClone(new CloneContext(cloneContext)); //PerformanceLogger.Pause(PerformanceStage.DeepClone); foreach (var composition in shaderMixinSource.Compositions) { //look for the key var foundVars = finalModule.FindAllVariablesByName(composition.Key).Where(value => value.Variable.Qualifiers.Contains(ParadoxStorageQualifier.Compose)).ToList(); if (foundVars.Count > 1) { log.Error(ParadoxMessageCode.ErrorAmbiguousComposition, new SourceSpan(), composition.Key); } else if (foundVars.Count > 0) { Variable foundVar = foundVars[0].Variable; var moduleMixins = BuildCompositionsDictionary(composition.Value, dictionary, compilationContext, cloneContext, log); if (moduleMixins == null) return null; dictionary.Add(foundVar, moduleMixins); } else { // No matching variable was found // TODO: log a message? } } return new List<ModuleMixin> { finalModule }; } if (shaderSource is ShaderClassSource) { var finalModule = compilationContext.GetModuleMixinFromShaderSource(shaderSource); //PerformanceLogger.Start(PerformanceStage.DeepClone); finalModule = finalModule.DeepClone(new CloneContext(cloneContext)); //PerformanceLogger.Pause(PerformanceStage.DeepClone); return new List<ModuleMixin> { finalModule }; } if (shaderSource is ShaderArraySource) { var shaderArraySource = shaderSource as ShaderArraySource; var compositionArray = new List<ModuleMixin>(); foreach (var shader in shaderArraySource.Values) { var mixin = BuildCompositionsDictionary(shader, dictionary, compilationContext, cloneContext, log); if (mixin == null) return null; compositionArray.AddRange(mixin); } return compositionArray; } return null; }
/// <summary> /// Check the name conflict between the two virtual tables /// </summary> public bool CheckNameConflict(MixinVirtualTable virtualTable, LoggerResult log) { var conflict = false; foreach (var variable in virtualTable.Variables.Where(variable => Variables.Any(x => x.Variable.Name.Text == variable.Variable.Name.Text))) { log.Error(ParadoxMessageCode.ErrorVariableNameConflict, variable.Variable.Span, variable.Variable, ""); conflict = true; } return conflict; }
/// <summary> /// Find the base definition of the method and override its occurence /// </summary> /// <param name="methodDeclaration"></param> /// <param name="errorLogger"></param> private void LookForBaseDeclarationMixin(MethodDeclaration methodDeclaration, LoggerResult errorLogger) { foreach (var dict in VirtualTableGroup.Select(x => x.Value)) { for (int i = 0; i < dict.Length; ++i) { var method = dict[i]; var baseDeclarationMixin = (string)method.GetTag(XenkoTags.BaseDeclarationMixin); // TODO: take typedefs into account... if (method.IsSameSignature(methodDeclaration)) { var sourceShader = ((ModuleMixin)methodDeclaration.GetTag(XenkoTags.ShaderScope)).MixinName; // test override if (methodDeclaration is MethodDefinition && method is MethodDefinition && !methodDeclaration.Qualifiers.Contains(XenkoStorageQualifier.Override)) errorLogger.Error(XenkoMessageCode.ErrorMissingOverride, method.Span, methodDeclaration, sourceShader); if (!(methodDeclaration is MethodDefinition)) errorLogger.Error(XenkoMessageCode.ErrorOverrindingDeclaration, method.Span, methodDeclaration, sourceShader); if (method.Qualifiers.Contains(XenkoStorageQualifier.Stage) && !methodDeclaration.Qualifiers.Contains(XenkoStorageQualifier.Stage)) { errorLogger.Warning(XenkoMessageCode.WarningMissingStageKeyword, methodDeclaration.Span, methodDeclaration, (methodDeclaration.GetTag(XenkoTags.ShaderScope) as ModuleMixin).MixinName); methodDeclaration.Qualifiers |= XenkoStorageQualifier.Stage; } else if (!method.Qualifiers.Contains(XenkoStorageQualifier.Stage) && methodDeclaration.Qualifiers.Contains(XenkoStorageQualifier.Stage)) { errorLogger.Error(XenkoMessageCode.ErrorExtraStageKeyword, methodDeclaration.Span, methodDeclaration, method, (methodDeclaration.GetTag(XenkoTags.ShaderScope) as ModuleMixin).MixinName); methodDeclaration.Qualifiers.Values.Remove(XenkoStorageQualifier.Stage); } dict[i] = methodDeclaration; methodDeclaration.SetTag(XenkoTags.BaseDeclarationMixin, baseDeclarationMixin); } } } }