/// <summary> /// Initializes a new instance of the <see cref="ShaderMixinParser"/> class. /// </summary> public ShaderMixinParser(IVirtualFileProvider fileProvider) { SourceManager = new ShaderSourceManager(fileProvider); var shaderLoader = new ShaderLoader(SourceManager); if (shaderLibrary == null) { shaderLibrary = new XenkoShaderLibrary(shaderLoader); } // Create the clone context with the instances of Hlsl classes HlslSemanticAnalysis.FillCloneContext(hlslCloneContext); }
/// <summary> /// Converts the specified hlsl source code to glsl. /// </summary> /// <param name="hlslSourcecode">The HLSL source code.</param> /// <param name="hlslEntryPoint">The HLSL entry point.</param> /// <param name="stage">The stage to convert.</param> /// <param name="shader">The shader.</param> /// <param name="inputHlslFilepath">The input HLSL filepath.</param> /// <returns> /// The resulting glsl AST tree. /// </returns> public global::SiliconStudio.Shaders.Ast.Shader Convert(string hlslSourcecode, string hlslEntryPoint, PipelineStage stage, string inputHlslFilepath = null) { try { // Convert from Framework.Graphics ShaderMacro to Framework.Shaders ShaderMacro var macros = new global::SiliconStudio.Shaders.Parser.ShaderMacro[Macros.Count]; for (int index = 0; index < Macros.Count; index++) { macros[index] = new global::SiliconStudio.Shaders.Parser.ShaderMacro(Macros[index].Name, Macros[index].Definition); } var result = HlslParser.TryPreProcessAndParse(hlslSourcecode, inputHlslFilepath, macros, IncludeDirectories); if (result.HasErrors) { throw new NotImplementedException("Logging"); //DisplayError(log, result, "Error while parsing file:"); return(null); } // Prepare the shader before type inference analysis HlslToGlslConvertor.Prepare(result.Shader); HlslSemanticAnalysis.Run(result); // If there are any type inference analysis, just display all errors but ytu if (result.HasErrors) { throw new NotImplementedException("Logging"); //DisplayError(log, result, "Error with type inferencing:"); } return(Convert(result, hlslEntryPoint, stage, inputHlslFilepath)); } catch (Exception ex) { throw new NotImplementedException("Logging"); //log.WriteLine("Unexpected error while converting file [{0}] with entry point [{1}] : {2}", inputHlslFilepath, hlslEntryPoint, ex.Message); return(null); } }
/// <summary> /// Converts the specified hlsl source code to glsl. /// </summary> /// <param name="hlslSourcecode">The HLSL source code.</param> /// <param name="hlslEntryPoint">The HLSL entry point.</param> /// <param name="stage">The stage to convert.</param> /// <param name="shader">The shader.</param> /// <param name="inputHlslFilepath">The input HLSL filepath.</param> /// <returns> /// The resulting glsl AST tree. /// </returns> public global::SiliconStudio.Shaders.Ast.Shader Convert(string hlslSourcecode, string hlslEntryPoint, PipelineStage stage, string inputHlslFilepath, IDictionary <int, string> inputAttributeNames, LoggerResult log) { try { // Convert from Framework.Graphics ShaderMacro to Framework.Shaders ShaderMacro var macros = new global::SiliconStudio.Shaders.Parser.ShaderMacro[Macros.Count]; for (int index = 0; index < Macros.Count; index++) { macros[index] = new global::SiliconStudio.Shaders.Parser.ShaderMacro(Macros[index].Name, Macros[index].Definition); } var result = HlslParser.TryPreProcessAndParse(hlslSourcecode, inputHlslFilepath, macros, IncludeDirectories); if (result.HasErrors) { log.Error(result.ToString()); return(null); } // Prepare the shader before type inference analysis HlslToGlslConvertor.Prepare(result.Shader); HlslSemanticAnalysis.Run(result); // If there are any type inference analysis, just display all errors but ytu if (result.HasErrors) { log.Error(result.ToString()); return(null); } return(Convert(result, hlslEntryPoint, stage, inputHlslFilepath, inputAttributeNames, log)); } catch (Exception ex) { log.Error("Unexpected error while converting file [{0}] with entry point [{1}]", ex, inputHlslFilepath, hlslEntryPoint); } return(null); }
/// <summary> /// Mixes shader parts to produces a single HLSL file shader. /// </summary> /// <param name="shaderMixinSource">The shader source.</param> /// <param name="macros">The shader perprocessor macros.</param> /// <param name="modifiedShaders">The list of modified shaders.</param> /// <returns>The combined shader in AST form.</returns> public ShaderMixinParsingResult Parse(ShaderMixinSource shaderMixinSource, Stride.Shaders.ShaderMacro[] macros = null) { // Make in-memory shader classes known to the source manager foreach (var x in shaderMixinSource.Mixins.OfType <ShaderClassString>()) { SourceManager.AddShaderSource(x.ClassName, x.ShaderSourceCode, x.ClassName); } // Creates a parsing result HashSet <ModuleMixinInfo> mixinsToAnalyze; ShaderMixinParsingResult parsingResult; var context = ParseAndAnalyze(shaderMixinSource, macros, out parsingResult, out mixinsToAnalyze); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } // Update the clone context in case new instances of classes are created CloneContext mixCloneContext; lock (hlslCloneContextLock) { if (hlslCloneContext == null) { hlslCloneContext = new CloneContext(); // Create the clone context with the instances of Hlsl classes HlslSemanticAnalysis.FillCloneContext(hlslCloneContext); } HlslSemanticAnalysis.UpdateCloneContext(hlslCloneContext); mixCloneContext = new CloneContext(hlslCloneContext); } // only clone once the stage classes foreach (var mixinInfo in mixinsToAnalyze) { foreach (var mixin in mixinInfo.Mixin.MinimalContext.Where(x => x.StageOnlyClass)) { mixin.DeepClone(mixCloneContext); } } // ---------------------------------------------------------- // Perform Shader Mixer // ---------------------------------------------------------- var externDict = new CompositionDictionary(); var finalModuleList = BuildCompositionsDictionary(shaderMixinSource, externDict, context, mixCloneContext, parsingResult); //PerformanceLogger.Stop(PerformanceStage.DeepClone); if (parsingResult.HasErrors) { return(parsingResult); } // look for stage compositions and add the links between variables and compositions when necessary var extraExternDict = new Dictionary <Variable, List <ModuleMixin> >(); foreach (var item in externDict) { if (item.Key.Qualifiers.Contains(StrideStorageQualifier.Stage)) { FullLinkStageCompositions(item.Key, item.Value, externDict, extraExternDict, parsingResult); } } foreach (var item in extraExternDict) { externDict.Add(item.Key, item.Value); } var mixinDictionary = BuildMixinDictionary(finalModuleList); if (finalModuleList != null) { var finalModule = finalModuleList[0]; //PerformanceLogger.Start(PerformanceStage.Mix); parsingResult.Reflection = new EffectReflection(); var mixer = new StrideShaderMixer(finalModule, parsingResult, mixinDictionary, externDict, new CloneContext(mixCloneContext)); mixer.Mix(); //PerformanceLogger.Stop(PerformanceStage.Mix); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } var finalShader = mixer.GetMixedShader(); // Simplifies the shader by removing dead code var simplifier = new ExpressionSimplifierVisitor(); simplifier.Run(finalShader); var sdShaderLinker = new ShaderLinker(parsingResult); sdShaderLinker.Run(finalShader); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } // Find all entry points // TODO: make this configurable by CompileParameters foreach (var stage in new[] { ShaderStage.Compute, ShaderStage.Vertex, ShaderStage.Hull, ShaderStage.Domain, ShaderStage.Geometry, ShaderStage.Pixel }) { var entryPoint = finalShader.Declarations.OfType <MethodDefinition>().FirstOrDefault(f => f.Attributes.OfType <AttributeDeclaration>().Any(a => a.Name == "EntryPoint" && (string)a.Parameters[0].Value == stage.ToString())); if (entryPoint == null) { continue; } parsingResult.EntryPoints[stage] = entryPoint.Name.Text; // When this is a compute shader, there is no need to scan other stages if (stage == ShaderStage.Compute) { break; } } var typeCleaner = new StrideShaderCleaner(); typeCleaner.Run(finalShader); //PerformanceLogger.Stop(PerformanceStage.Global); //PerformanceLogger.PrintLastResult(); //SemanticPerformance.PrintResult(); //MixPerformance.PrintResult(); //GenerateShaderPerformance.PrintResult(); //StreamCreatorPerformance.PrintResult(); //ShaderLoader.PrintTime(); //PerformanceLogger.WriteOut(52); parsingResult.Shader = finalShader; } return(parsingResult); }
/// <summary> /// Mixes shader parts to produces a single HLSL file shader. /// </summary> /// <param name="shaderMixinSource">The shader source.</param> /// <param name="macros">The shader perprocessor macros.</param> /// <param name="modifiedShaders">The list of modified shaders.</param> /// <returns>The combined shader in AST form.</returns> public ShaderMixinParsingResult Parse(ShaderMixinSource shaderMixinSource, Paradox.Shaders.ShaderMacro[] macros = null, HashSet <string> modifiedShaders = null) { //SemanticPerformance.Reset(); //PerformanceLogger.Reset(); //MixPerformance.Reset(); //GenerateShaderPerformance.Reset(); //StreamCreatorPerformance.Reset(); // updates the list of modified shaders. shaderLibrary.ModifiedShaders = modifiedShaders; // Creates a parsing result var parsingResult = new ShaderMixinParsingResult(); SiliconStudio.Shaders.Parser.ShaderMacro[] macrosParser; if (macros == null) { macrosParser = new SiliconStudio.Shaders.Parser.ShaderMacro[0]; } else { macrosParser = new SiliconStudio.Shaders.Parser.ShaderMacro[macros.Length]; for (var i = 0; i < macros.Length; ++i) { macrosParser[i] = new SiliconStudio.Shaders.Parser.ShaderMacro(macros[i].Name, macros[i].Definition); } } //PerformanceLogger.Start(PerformanceStage.Global); // ---------------------------------------------------------- // Load all shaders // ---------------------------------------------------------- HashSet <ModuleMixinInfo> mixinsToAnalyze; lock (shaderLibrary) { //PerformanceLogger.Start(PerformanceStage.Loading); mixinsToAnalyze = shaderLibrary.LoadShaderSource(shaderMixinSource, macrosParser); //PerformanceLogger.Stop(PerformanceStage.Loading); } // Extract all ModuleMixinInfo and check for any errors var allMixinInfos = new HashSet <ModuleMixinInfo>(); foreach (var moduleMixinInfo in mixinsToAnalyze) { allMixinInfos.UnionWith(moduleMixinInfo.MinimalContext); } foreach (var moduleMixinInfo in allMixinInfos) { moduleMixinInfo.Log.CopyTo(parsingResult); var ast = moduleMixinInfo.MixinAst; var shaderClassSource = moduleMixinInfo.ShaderSource as ShaderClassSource; if (ast != null && shaderClassSource != null) { var sourcePath = SourceManager.FindFilePath(shaderClassSource.ClassName); if (sourcePath == null) { throw new InvalidOperationException(string.Format("Can't find source path for class {0}", shaderClassSource.ClassName)); } parsingResult.HashSources[sourcePath] = ast.SourceHash; } } // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } // ---------------------------------------------------------- // Perform Type Analysis // ---------------------------------------------------------- //PerformanceLogger.Start(PerformanceStage.TypeAnalysis); var context = GetCompilationContext(mixinsToAnalyze, parsingResult); //PerformanceLogger.Stop(PerformanceStage.TypeAnalysis); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } lock (SemanticAnalyzerLock) { //PerformanceLogger.Start(PerformanceStage.SemanticAnalysis); //SemanticPerformance.Start(SemanticStage.Global); foreach (var mixin in mixinsToAnalyze) { context.Analyze(mixin); } //SemanticPerformance.Pause(SemanticStage.Global); //PerformanceLogger.Stop(PerformanceStage.SemanticAnalysis); } // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } // Update the clone context in case new instances of classes are created lock (hlslCloneContext) { HlslSemanticAnalysis.UpdateCloneContext(hlslCloneContext); } // only clone once the stage classes var mixCloneContext = new CloneContext(hlslCloneContext); foreach (var mixinInfo in mixinsToAnalyze) { foreach (var mixin in mixinInfo.Mixin.MinimalContext.Where(x => x.StageOnlyClass)) { mixin.DeepClone(mixCloneContext); } } // ---------------------------------------------------------- // Perform Shader Mixer // ---------------------------------------------------------- var externDict = new Dictionary <Variable, List <ModuleMixin> >(); var finalModuleList = BuildCompositionsDictionary(shaderMixinSource, externDict, context, mixCloneContext); //PerformanceLogger.Stop(PerformanceStage.DeepClone); var mixinDictionary = BuildMixinDictionary(finalModuleList); if (finalModuleList != null) { var finalModule = finalModuleList[0]; //PerformanceLogger.Start(PerformanceStage.Mix); var mixer = new ParadoxShaderMixer(finalModule, parsingResult, mixinDictionary, externDict, new CloneContext(mixCloneContext)); mixer.Mix(); //PerformanceLogger.Stop(PerformanceStage.Mix); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } var finalShader = mixer.GetMixedShader(); parsingResult.Reflection = new EffectReflection(); var pdxShaderLinker = new ShaderLinker(parsingResult); pdxShaderLinker.Run(finalShader); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } // Find all entry points // TODO: make this configurable by CompileParameters foreach (var stage in new[] { ShaderStage.Compute, ShaderStage.Vertex, ShaderStage.Hull, ShaderStage.Domain, ShaderStage.Geometry, ShaderStage.Pixel }) { var entryPoint = finalShader.Declarations.OfType <MethodDefinition>().FirstOrDefault(f => f.Attributes.OfType <AttributeDeclaration>().Any(a => a.Name == "EntryPoint" && (string)a.Parameters[0].Value == stage.ToString())); if (entryPoint == null) { continue; } parsingResult.EntryPoints[stage] = entryPoint.Name.Text; // When this is a compute shader, there is no need to scan other stages if (stage == ShaderStage.Compute) { break; } } var typeCleaner = new ParadoxShaderCleaner(); typeCleaner.Run(finalShader); //PerformanceLogger.Stop(PerformanceStage.Global); //PerformanceLogger.PrintLastResult(); //SemanticPerformance.PrintResult(); //MixPerformance.PrintResult(); //GenerateShaderPerformance.PrintResult(); //StreamCreatorPerformance.PrintResult(); //ShaderLoader.PrintTime(); //PerformanceLogger.WriteOut(52); parsingResult.Shader = finalShader; } return(parsingResult); }
/// <summary> /// Mixes shader parts to produces a single HLSL file shader. /// </summary> /// <param name="shaderMixinSource">The shader source.</param> /// <param name="macros">The shader perprocessor macros.</param> /// <param name="modifiedShaders">The list of modified shaders.</param> /// <returns>The combined shader in AST form.</returns> public ShaderMixinParsingResult Parse(ShaderMixinSource shaderMixinSource, Paradox.Shaders.ShaderMacro[] macros = null) { // Creates a parsing result HashSet <ModuleMixinInfo> mixinsToAnalyze; ShaderMixinParsingResult parsingResult; var context = ParseAndAnalyze(shaderMixinSource, macros, out parsingResult, out mixinsToAnalyze); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } // Update the clone context in case new instances of classes are created lock (hlslCloneContext) { HlslSemanticAnalysis.UpdateCloneContext(hlslCloneContext); } // only clone once the stage classes var mixCloneContext = new CloneContext(hlslCloneContext); foreach (var mixinInfo in mixinsToAnalyze) { foreach (var mixin in mixinInfo.Mixin.MinimalContext.Where(x => x.StageOnlyClass)) { mixin.DeepClone(mixCloneContext); } } // ---------------------------------------------------------- // Perform Shader Mixer // ---------------------------------------------------------- var externDict = new Dictionary <Variable, List <ModuleMixin> >(); var finalModuleList = BuildCompositionsDictionary(shaderMixinSource, externDict, context, mixCloneContext); //PerformanceLogger.Stop(PerformanceStage.DeepClone); var mixinDictionary = BuildMixinDictionary(finalModuleList); if (finalModuleList != null) { var finalModule = finalModuleList[0]; //PerformanceLogger.Start(PerformanceStage.Mix); var mixer = new ParadoxShaderMixer(finalModule, parsingResult, mixinDictionary, externDict, new CloneContext(mixCloneContext)); mixer.Mix(); //PerformanceLogger.Stop(PerformanceStage.Mix); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } var finalShader = mixer.GetMixedShader(); parsingResult.Reflection = new EffectReflection(); var pdxShaderLinker = new ShaderLinker(parsingResult); pdxShaderLinker.Run(finalShader); // Return directly if there was any errors if (parsingResult.HasErrors) { return(parsingResult); } // Find all entry points // TODO: make this configurable by CompileParameters foreach (var stage in new[] { ShaderStage.Compute, ShaderStage.Vertex, ShaderStage.Hull, ShaderStage.Domain, ShaderStage.Geometry, ShaderStage.Pixel }) { var entryPoint = finalShader.Declarations.OfType <MethodDefinition>().FirstOrDefault(f => f.Attributes.OfType <AttributeDeclaration>().Any(a => a.Name == "EntryPoint" && (string)a.Parameters[0].Value == stage.ToString())); if (entryPoint == null) { continue; } parsingResult.EntryPoints[stage] = entryPoint.Name.Text; // When this is a compute shader, there is no need to scan other stages if (stage == ShaderStage.Compute) { break; } } var typeCleaner = new ParadoxShaderCleaner(); typeCleaner.Run(finalShader); //PerformanceLogger.Stop(PerformanceStage.Global); //PerformanceLogger.PrintLastResult(); //SemanticPerformance.PrintResult(); //MixPerformance.PrintResult(); //GenerateShaderPerformance.PrintResult(); //StreamCreatorPerformance.PrintResult(); //ShaderLoader.PrintTime(); //PerformanceLogger.WriteOut(52); parsingResult.Shader = finalShader; } return(parsingResult); }