/// <summary>
        /// Constructor
        /// </summary>
        /// <param name="moduleMixin">the final shader information</param>
        /// <param name="log">The log.</param>
        /// <param name="context">all the mixins in the context</param>
        /// <param name="compositionsPerVariable">The compositions per variable.</param>
        /// <param name="cloneContext">The clone context.</param>
        /// <exception cref="System.ArgumentNullException">
        /// moduleMixin
        /// or
        /// log
        /// or
        /// context
        /// </exception>
        public XenkoShaderMixer(ModuleMixin moduleMixin, LoggerResult log, Dictionary<string, ModuleMixin> context, CompositionDictionary compositionsPerVariable, CloneContext cloneContext = null)
        {
            if (moduleMixin == null)
                throw new ArgumentNullException("moduleMixin");

            if (log == null) 
                throw new ArgumentNullException("log");

            if (context == null)
                throw new ArgumentNullException("context");

            this.log = log;

            mixContext = context;
            mainModuleMixin = moduleMixin;
            defaultCloneContext = cloneContext;

            if (compositionsPerVariable != null)
                CompositionsPerVariable = compositionsPerVariable;
            else
                CompositionsPerVariable = new CompositionDictionary();

            var mixinsToAnalyze = new Stack<ModuleMixin>(CompositionsPerVariable.Values.SelectMany(x => x));
            mixinsToAnalyze.Push(mainModuleMixin);

            while (mixinsToAnalyze.Count > 0)
                AddDefaultCompositions(mixinsToAnalyze);
        }
Beispiel #2
0
    private void CheckInitialize(SerializedProperty property, GUIContent label)
    {
        if (_Dictionary == null)
        {
            var target = property.serializedObject.targetObject;
            _Dictionary = fieldInfo.GetValue(target) as CompositionDictionary <TK, TV>;
            if (_Dictionary == null)
            {
                _Dictionary = new CompositionDictionary <TK, TV>();
                fieldInfo.SetValue(target, _Dictionary);
            }

            _Foldout = EditorPrefs.GetBool(label.text);
        }
    }
Beispiel #3
0
        /// <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(StrideTags.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(StrideStorageQualifier.Compose)).ToList();
                            if (foundVars.Count > 1)
                            {
                                log.Error(StrideMessageCode.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?
                            }
                        }
                    }
                }
            }
        }
Beispiel #4
0
        /// <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(StrideStorageQualifier.Compose)).ToList();

                    if (foundVars.Count > 1)
                    {
                        log.Error(StrideMessageCode.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 ShaderClassCode)
            {
                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);
        }
Beispiel #5
0
        /// <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>
        /// 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>
        /// 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 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(ParadoxStorageQualifier.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);
                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();

                // Simplifies the shader by removing dead code
                var simplifier = new ExpressionSimplifierVisitor();
                simplifier.Run(finalShader);

                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;
        }