Esempio n. 1
0
        public static EffectFile Read(string filename)
        {
            filename = Path.GetFullPath(filename);
            var txt = File.ReadAllText(filename);

            txt = ProcessIncludes(Path.GetFileName(filename), txt, Path.GetDirectoryName(filename));
            if (txt == null)
            {
                return(null);
            }
            var           effectFile = new EffectFile();
            List <string> features   = new List <string>();

            using (var reader = new StringReader(txt))
            {
                bool          inMultilineComment = false;
                StringBuilder currentBlock       = null;
                string        currentBlockName   = null;
                bool          blockIsFragment    = false;
                int           lineNumber         = 1;
                string        ln;
                while ((ln = reader.ReadLine()) != null)
                {
                    int mlEnd = -1;
                    if (!inMultilineComment)
                    {
                        var mlStart = ln.IndexOf("/*", StringComparison.Ordinal);
                        if (mlStart != -1)
                        {
                            mlEnd = ln.IndexOf("*/", StringComparison.Ordinal);
                            if (mlEnd == -1)
                            {
                                inMultilineComment = true;
                            }
                        }
                    }
                    else
                    {
                        mlEnd = ln.IndexOf("*/", StringComparison.Ordinal);
                        if (mlEnd != -1)
                        {
                            inMultilineComment = false;
                        }
                    }
                    var idx = ln.IndexOf('@');
                    if (idx != -1 && !inMultilineComment && mlEnd < 0)
                    {
                        bool valid = true;
                        for (int i = 0; i < idx; i++)
                        {
                            if (!char.IsWhiteSpace(ln[i]))
                            {
                                valid = false;
                                break;
                            }
                        }
                        if (valid)
                        {
                            var directive = ln.Substring(idx + 1).Trim();
                            directive = directive.Replace("\t", " ");
                            var vals = directive.Split(' ', StringSplitOptions.RemoveEmptyEntries);
                            if (vals.Length > 0)
                            {
                                switch (vals[0].ToLowerInvariant())
                                {
                                case "name":
                                case "vertex":
                                case "fragment":
                                case "feature":
                                case "lazy":
                                    if (currentBlock != null)
                                    {
                                        if (blockIsFragment)
                                        {
                                            effectFile.FragmentSource = currentBlock.ToString();
                                        }
                                        else
                                        {
                                            effectFile.VertexSource = currentBlock.ToString();
                                        }
                                    }
                                    currentBlock = null;
                                    break;
                                }
                                switch (vals[0].ToLowerInvariant())
                                {
                                case "name":
                                    effectFile.Name = directive.Substring(directive.IndexOf("name") + 4).Trim();
                                    break;

                                case "vertex":
                                    if (effectFile.VertexSource != null)
                                    {
                                        Console.Error.WriteLine($"Duplicate vertex block at {lineNumber}");
                                        return(null);
                                    }
                                    currentBlock    = new StringBuilder();
                                    blockIsFragment = false;
                                    break;

                                case "fragment":
                                    if (effectFile.FragmentSource != null)
                                    {
                                        Console.Error.WriteLine($"Duplicate fragment block at {lineNumber}");
                                        return(null);
                                    }
                                    currentBlock    = new StringBuilder();
                                    blockIsFragment = true;
                                    break;

                                case "feature":
                                    features.Add(vals[1]);
                                    break;

                                case "lazy":
                                    effectFile.Lazy = true;
                                    break;

                                default:
                                    Console.Error.WriteLine($"Invalid directive {ln} at {lineNumber}");
                                    break;
                                }
                            }
                        }
                    }
                    else
                    {
                        if (currentBlock != null)
                        {
                            currentBlock.AppendLine(ln);
                        }
                    }
                    lineNumber++;
                }
                if (currentBlock != null)
                {
                    if (blockIsFragment)
                    {
                        effectFile.FragmentSource = currentBlock.ToString();
                    }
                    else
                    {
                        effectFile.VertexSource = currentBlock.ToString();
                    }
                }
            }
            if (string.IsNullOrWhiteSpace(effectFile.Name))
            {
                effectFile.Name = Path.GetFileNameWithoutExtension(filename);
            }
            effectFile.Name     = SaneName(effectFile.Name);
            effectFile.Features = features.ToArray();
            if (effectFile.VertexSource == null)
            {
                Console.Error.WriteLine("Vertex source not specified");
                return(null);
            }
            if (effectFile.FragmentSource == null)
            {
                Console.Error.WriteLine("Fragment source not specified");
                return(null);
            }
            return(effectFile);
        }
Esempio n. 2
0
        static int Main(string[] args)
        {
            bool   shouldShowHelp = false;
            var    codeOpts       = new CodeGenOptions();
            string glslValidator  = "glslangValidator";
            var    imports        = new List <string>();
            var    options        = new OptionSet
            {
                { "o|output=", "output directory", n => codeOpts.OutputDirectory = n },
                { "g|glslangValidator=", "glslangValidator path", g => glslValidator = g },
                { "b|brotli", "compress with brotli (.NET Core only)", b => codeOpts.Brotli = b != null },
                { "l|log", "generate logging code", l => codeOpts.Log = l != null },
                { "x|logmethod=", "logging method name", x => codeOpts.LogMethod = x.Trim() },
                { "t|type=", "shader type name", t => codeOpts.ShaderType = t.Trim() },
                { "i|import=", "import namespace", i => imports.Add(i.Trim()) },
                { "n|namespace=", "generated code namespace", n => codeOpts.Namespace = n.Trim() },
                { "c|compilemethod=", "shader compile method name", c => codeOpts.ShaderCompileMethod = c.Trim() },
                { "p|private", "generate internal classes", p => codeOpts.Public = p == null },
                { "h|help", "show this message and exit", h => shouldShowHelp = h != null },
            };
            List <string> input = null;

            try
            {
                input = options.Parse(args);
            }
            catch (OptionException e)
            {
                Console.Write("shaderprocessor: ");
                Console.WriteLine(e.Message);
                Console.WriteLine("Try `shaderprocessor --help' for more information.");
                return(1);
            }
            if (shouldShowHelp || input?.Count < 1)
            {
                WriteHelp(options);
                return(0);
            }
            if (string.IsNullOrEmpty(codeOpts.OutputDirectory))
            {
                Console.WriteLine("Output directory must be specified.");
                Console.WriteLine("Try `shaderprocessor --help' for more information.");
                return(2);
            }

            foreach (var i in input)
            {
                if (!File.Exists(i))
                {
                    Console.Error.WriteLine($"File does not exist {i}");
                    return(2);
                }
            }

            codeOpts.Imports = imports.ToArray();
            List <EffectFile> effects = new List <EffectFile>();

            foreach (var i in input)
            {
                var fx = EffectFile.Read(i);
                if (fx == null)
                {
                    Console.Error.WriteLine($"Error reading file {i}");
                    return(1);
                }
                effects.Add(fx);
            }
            //Don't rely on filesystem sorting, makes merges less crappy
            effects.Sort((x, y) => String.Compare(x.Name, y.Name, StringComparison.Ordinal));

            Glslang.ToolPath = GetPath(glslValidator);
            if (Glslang.ToolPath != null)
            {
                foreach (var fx in effects)
                {
                    for (int i = 0; i < 2; i++)
                    {
                        //Validate for 310 es and 430
                        var ver = (i == 1) ? "430" : "310 es\nprecision highp float;\nprecision highp int;";
                        var vs  = (i == 1) ? new[] { "VERTEX_SHADER", "FEATURES430" } : new[] { "VERTEX_SHADER" };
                        var fs  = (i == 1) ? new[] { "FRAGMENT_SHADER", "FEATURES430" } : new[] { "FRAGMENT_SHADER" };
                        //Default defines
                        //VERTEX_SHADER
                        //FRAGMENT_SHADER
                        if (!Glslang.ValidateShader($"{fx.Name} vertex shader", "vert",
                                                    InsertDefine(fx.VertexSource, ver, vs)))
                        {
                            return(1);
                        }
                        if (!Glslang.ValidateShader($"{fx.Name} fragment shader", "frag",
                                                    InsertDefine(fx.FragmentSource, ver, fs)))
                        {
                            return(1);
                        }
                        //Validate syntax for all combinations of preprocessor defs
                        foreach (var featureSet in FeatureHelper.Permute(null, fx.Features))
                        {
                            var vdefs  = vs.Concat(featureSet);
                            var fdefs  = fs.Concat(featureSet);
                            var vsName = $"{fx.Name} vertex shader ({string.Join(" | ", featureSet)})";
                            var fsName = $"{fx.Name} fragment shader ({string.Join(" | ", featureSet)}";
                            if (!Glslang.ValidateShader(vsName, "vert", InsertDefine(fx.VertexSource, ver, vdefs)))
                            {
                                return(1);
                            }
                            if (!Glslang.ValidateShader(fsName, "frag", InsertDefine(fx.FragmentSource, ver, fdefs)))
                            {
                                return(1);
                            }
                        }
                    }
                    fx.VertexSource   = "#define VERTEX_SHADER\n#line 1\n" + fx.VertexSource;
                    fx.FragmentSource = "#define FRAGMENT_SHADER\n#line 1\n" + fx.FragmentSource;
                }
            }
            else
            {
                Console.Error.WriteLine("WARNING: Glslang not found. Skipping validation.");
            }
            Generate(codeOpts, effects);
            return(0);
        }
Esempio n. 3
0
        public static string Generate(CodeGenOptions opts, EffectFile fx, Dictionary <string, int> enumVals)
        {
            var compileUnit = new CodeCompileUnit();
            var nsroot      = new CodeNamespace(opts.Namespace);

            nsroot.Imports.Add(new CodeNamespaceImport("System"));
            foreach (var import in opts.Imports)
            {
                nsroot.Imports.Add(new CodeNamespaceImport(import));
            }
            compileUnit.Namespaces.Add(nsroot);
            var genclass = new CodeTypeDeclaration(fx.Name);

            if (!opts.Public)
            {
                genclass.TypeAttributes = TypeAttributes.Class;
            }
            nsroot.Types.Add(genclass);
            var vsrc = new CodeMemberField(typeof(byte[]), "vertex_bytes");

            vsrc.Attributes     = MemberAttributes.Private | MemberAttributes.Static;
            vsrc.InitExpression = ByteArray(Compress.GetBytes(fx.VertexSource, opts.Brotli));
            genclass.Members.Add(vsrc);

            var fsrc = new CodeMemberField(typeof(byte[]), "fragment_bytes");

            fsrc.Attributes     = MemberAttributes.Private | MemberAttributes.Static;
            fsrc.InitExpression = ByteArray(Compress.GetBytes(fx.FragmentSource, opts.Brotli));
            genclass.Members.Add(fsrc);


            var variants = new CodeMemberField(new CodeTypeReference(opts.ShaderType, 1), "variants");

            variants.Attributes = MemberAttributes.Static;
            genclass.Members.Add(variants);

            var iscompiled = new CodeMemberField(typeof(bool), "iscompiled");

            iscompiled.Attributes     = MemberAttributes.Private | MemberAttributes.Static;
            iscompiled.InitExpression = new CodePrimitiveExpression(false);
            genclass.Members.Add(iscompiled);

            int mask = 0;

            foreach (var feature in fx.Features)
            {
                mask |= enumVals[feature];
            }

            var getIdx = new CodeMemberMethod();

            getIdx.Name       = "GetIndex";
            getIdx.Attributes = MemberAttributes.Private | MemberAttributes.Static;
            getIdx.ReturnType = new CodeTypeReference(typeof(int));
            getIdx.Parameters.Add(new CodeParameterDeclarationExpression("ShaderFeatures", "features"));
            //Mask out invalid flags
            getIdx.Statements.Add(new CodeVariableDeclarationStatement(
                                      "ShaderFeatures", "masked",
                                      new CodeBinaryOperatorExpression(new CodeArgumentReferenceExpression("features"),
                                                                       CodeBinaryOperatorType.BitwiseAnd,
                                                                       new CodeCastExpression("ShaderFeatures", new CodePrimitiveExpression(mask)))));
            //Continue
            int idx = 1;

            foreach (var permutation in FeatureHelper.Permute("", fx.Features))
            {
                int flag = 0;
                foreach (var s in permutation)
                {
                    flag |= enumVals[s];
                }
                var expr = new CodeCastExpression("ShaderFeatures", new CodePrimitiveExpression(flag));
                var cond = new CodeConditionStatement(new CodeBinaryOperatorExpression(
                                                          new CodeArgumentReferenceExpression("masked"),
                                                          CodeBinaryOperatorType.ValueEquality, expr),
                                                      new CodeMethodReturnStatement(new CodePrimitiveExpression(idx++)));
                getIdx.Statements.Add(cond);
            }
            getIdx.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(0)));
            genclass.Members.Add(getIdx);

            var getShader = new CodeMemberMethod();

            getShader.Name       = "Get";
            getShader.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            getShader.ReturnType = new CodeTypeReference(opts.ShaderType);
            getShader.Parameters.Add(new CodeParameterDeclarationExpression("ShaderFeatures", "features"));
            var retval = new CodeArrayIndexerExpression(new CodeFieldReferenceExpression(null, "variants"),
                                                        new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "GetIndex"),
                                                                                       new CodeArgumentReferenceExpression("features")));

            if (fx.Lazy)
            {
                var stmt = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "Compile"));
                getShader.Statements.Add(stmt);
            }
            getShader.Statements.Add(new CodeMethodReturnStatement(retval));
            genclass.Members.Add(getShader);

            var getZero = new CodeMemberMethod();

            getZero.Name       = "Get";
            getZero.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            getZero.ReturnType = new CodeTypeReference(opts.ShaderType);
            if (fx.Lazy)
            {
                var stmt = new CodeMethodInvokeExpression(new CodeMethodReferenceExpression(null, "Compile"));
                getZero.Statements.Add(stmt);
            }
            var zeroRet = new CodeArrayIndexerExpression(new CodeFieldReferenceExpression(null, "variants"),
                                                         new CodePrimitiveExpression(0));

            getZero.Statements.Add(new CodeMethodReturnStatement(zeroRet));
            genclass.Members.Add(getZero);

            getShader.ReturnType = new CodeTypeReference(opts.ShaderType);
            var compile = new CodeMemberMethod();

            compile.Name       = "Compile";
            compile.Attributes = MemberAttributes.Public | MemberAttributes.Static;
            //Once only
            compile.Statements.Add(new CodeConditionStatement(new CodeFieldReferenceExpression(null, "iscompiled"),
                                                              new CodeMethodReturnStatement()));
            compile.Statements.Add(new CodeAssignStatement(
                                       new CodeFieldReferenceExpression(null, "iscompiled"),
                                       new CodePrimitiveExpression(true)
                                       ));
            //Log
            if (opts.Log)
            {
                compile.Statements.Add(
                    new CodeMethodInvokeExpression(null, opts.LogMethod,
                                                   new CodePrimitiveExpression($"Compiling {fx.Name}"))
                    );
            }
            //Decompress code
            compile.Statements.Add(new CodeVariableDeclarationStatement(typeof(string), "vertsrc"));
            compile.Statements.Add(new CodeVariableDeclarationStatement(typeof(string), "fragsrc"));
            compile.Statements.Add(new CodeAssignStatement(
                                       new CodeVariableReferenceExpression("vertsrc"),
                                       new CodeMethodInvokeExpression(null, "ShCompHelper.FromArray",
                                                                      new CodeFieldReferenceExpression(null, "vertex_bytes"))
                                       )
                                   );
            compile.Statements.Add(new CodeAssignStatement(
                                       new CodeVariableReferenceExpression("fragsrc"),
                                       new CodeMethodInvokeExpression(null, "ShCompHelper.FromArray",
                                                                      new CodeFieldReferenceExpression(null, "fragment_bytes"))
                                       )
                                   );
            var vertRef  = new CodeVariableReferenceExpression("vertsrc");
            var fragRef  = new CodeVariableReferenceExpression("fragsrc");
            var compMeth = new CodeMethodReferenceExpression(null, opts.ShaderCompileMethod);

            //Init array
            compile.Statements.Add(new CodeAssignStatement(
                                       new CodeFieldReferenceExpression(null, "variants"),
                                       new CodeArrayCreateExpression(opts.ShaderType, idx))
                                   );
            //Compile null variant
            compile.Statements.Add(new CodeAssignStatement(
                                       new CodeArrayIndexerExpression(new CodeFieldReferenceExpression(null, "variants"), new CodePrimitiveExpression(0)),
                                       new CodeMethodInvokeExpression(compMeth, vertRef, fragRef, new CodePrimitiveExpression(""))
                                       ));
            //Compile all variants
            idx = 1;
            foreach (var permutation in FeatureHelper.Permute(null, fx.Features))
            {
                var builder = new StringBuilder();
                builder.AppendLine();
                foreach (var def in permutation)
                {
                    builder.Append("#define ").AppendLine(def);
                }
                builder.AppendLine("#line 1");
                compile.Statements.Add(new CodeAssignStatement(
                                           new CodeArrayIndexerExpression(new CodeFieldReferenceExpression(null, "variants"), new CodePrimitiveExpression(idx++)),
                                           new CodeMethodInvokeExpression(compMeth, vertRef, fragRef, new CodePrimitiveExpression(builder.ToString()))
                                           ));
            }
            genclass.Members.Add(compile);
            return(GenCodeUnit(compileUnit));
        }