Beispiel #1
0
        private static string BackendExtension(LanguageBackend lang)
        {
            if (lang.GetType() == typeof(HlslBackend))
            {
                return("hlsl");
            }
            else if (lang.GetType() == typeof(Glsl330Backend))
            {
                return("330.glsl");
            }
            else if (lang.GetType() == typeof(GlslEs300Backend))
            {
                return("300.glsles");
            }
            else if (lang.GetType() == typeof(Glsl450Backend))
            {
                return("450.glsl");
            }
            else if (lang.GetType() == typeof(MetalBackend))
            {
                return("metal");
            }

            throw new InvalidOperationException("Invalid backend type: " + lang.GetType().Name);
        }
Beispiel #2
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TestSet"/> class.
 /// </summary>
 /// <param name="testSets">The test sets.</param>
 /// <param name="toolChain">The tool chain (if any).</param>
 public TestSet(TestSets testSets, ToolChain toolChain = null)
 {
     TestSets  = testSets;
     Name      = toolChain?.GraphicsBackend.ToString() ?? "CPU";
     ToolChain = toolChain;
     Backend   = toolChain?.CreateBackend(testSets.Compilation);
 }
Beispiel #3
0
        private static bool CompileCode(LanguageBackend lang, string shaderPath, string entryPoint, ShaderFunctionType type, out string[] paths)
        {
            Type langType = lang.GetType();

            if (langType == typeof(HlslBackend) && IsFxcAvailable())
            {
                bool result = CompileHlsl(shaderPath, entryPoint, type, out string path);
                paths = new[] { path };
                return(result);
            }
            else if (langType == typeof(Glsl450Backend) && IsGlslangValidatorAvailable())
            {
                bool result = CompileSpirv(shaderPath, entryPoint, type, out string path);
                paths = new[] { path };
                return(result);
            }
            else if (langType == typeof(MetalBackend) && AreMetalMacOSToolsAvailable())
            {
                bool macOSresult = CompileMetal(shaderPath, true, out string pathMacOS);
                bool iosResult   = CompileMetal(shaderPath, false, out string pathiOS);
                paths = new[] { pathMacOS, pathiOS };
                return(macOSresult && iosResult);
            }
            else
            {
                paths = Array.Empty <string>();
                return(false);
            }
        }
        public static void ShaderSetAutoDiscovery()
        {
            ToolChain toolChain = ToolChain.Get(ToolFeatures.ToCompiled);

            if (toolChain == null)
            {
                throw new RequiredToolFeatureMissingException("No tool chain supporting compilation was found!");
            }

            Compilation                        compilation      = TestUtil.GetCompilation();
            LanguageBackend                    backend          = toolChain.CreateBackend(compilation);
            ShaderGenerator                    sg               = new ShaderGenerator(compilation, backend);
            ShaderGenerationResult             generationResult = sg.GenerateShaders();
            IReadOnlyList <GeneratedShaderSet> hlslSets         = generationResult.GetOutput(backend);

            Assert.Equal(4, hlslSets.Count);
            GeneratedShaderSet set = hlslSets[0];

            Assert.Equal("VertexAndFragment", set.Name);

            CompileResult result = toolChain.Compile(set.VertexShaderCode, Stage.Vertex, "VS");

            Assert.False(result.HasError, result.ToString());

            result = toolChain.Compile(set.FragmentShaderCode, Stage.Fragment, "FS");
            Assert.False(result.HasError, result.ToString());
        }
        public void AllSetsAllLanguagesEndToEnd()
        {
            Compilation compilation = TestUtil.GetTestProjectCompilation();

            LanguageBackend[] backends = new LanguageBackend[]
            {
                new HlslBackend(compilation),
                new Glsl330Backend(compilation),
                new Glsl450Backend(compilation),
            };
            ShaderGenerator sg = new ShaderGenerator(compilation, backends);

            ShaderGenerationResult result = sg.GenerateShaders();

            foreach (LanguageBackend backend in backends)
            {
                IReadOnlyList <GeneratedShaderSet> sets = result.GetOutput(backend);
                foreach (GeneratedShaderSet set in sets)
                {
                    if (set.VertexShaderCode != null)
                    {
                        if (backend is HlslBackend)
                        {
                            FxcTool.AssertCompilesCode(set.VertexShaderCode, "vs_5_0", set.VertexFunction.Name);
                        }
                        else
                        {
                            bool is450 = backend is Glsl450Backend;
                            GlsLangValidatorTool.AssertCompilesCode(set.VertexShaderCode, "vert", is450);
                        }
                    }
                    if (set.FragmentFunction != null)
                    {
                        if (backend is HlslBackend)
                        {
                            FxcTool.AssertCompilesCode(set.FragmentShaderCode, "ps_5_0", set.FragmentFunction.Name);
                        }
                        else
                        {
                            bool is450 = backend is Glsl450Backend;
                            GlsLangValidatorTool.AssertCompilesCode(set.FragmentShaderCode, "frag", is450);
                        }
                    }
                    if (set.ComputeFunction != null)
                    {
                        if (backend is HlslBackend)
                        {
                            FxcTool.AssertCompilesCode(set.ComputeShaderCode, "cs_5_0", set.ComputeFunction.Name);
                        }
                        else
                        {
                            bool is450 = backend is Glsl450Backend;
                            GlsLangValidatorTool.AssertCompilesCode(set.ComputeShaderCode, "comp", is450);
                        }
                    }
                }
            }
        }
Beispiel #6
0
        private void TestCompile(GraphicsBackend graphicsBackend, string vsName, string fsName, string csName = null)
        {
            Compilation compilation = TestUtil.GetCompilation();
            ToolChain   toolChain   = ToolChain.Require(ToolFeatures.ToCompiled, graphicsBackend);

            LanguageBackend backend = toolChain.CreateBackend(compilation);
            ShaderGenerator sg      = new ShaderGenerator(compilation, backend, vsName, fsName, csName);

            ShaderGenerationResult generationResult = sg.GenerateShaders();

            IReadOnlyList <GeneratedShaderSet> sets = generationResult.GetOutput(backend);

            Assert.Equal(1, sets.Count);
            GeneratedShaderSet set         = sets[0];
            ShaderModel        shaderModel = set.Model;

            List <CompileResult> results = new List <CompileResult>();

            if (!string.IsNullOrWhiteSpace(vsName))
            {
                ShaderFunction vsFunction = shaderModel.GetFunction(vsName);
                string         vsCode     = set.VertexShaderCode;

                results.Add(toolChain.Compile(vsCode, Stage.Vertex, vsFunction.Name));
            }
            if (!string.IsNullOrWhiteSpace(fsName))
            {
                ShaderFunction fsFunction = shaderModel.GetFunction(fsName);
                string         fsCode     = set.FragmentShaderCode;
                results.Add(toolChain.Compile(fsCode, Stage.Fragment, fsFunction.Name));
            }
            if (!string.IsNullOrWhiteSpace(csName))
            {
                ShaderFunction csFunction = shaderModel.GetFunction(csName);
                string         csCode     = set.ComputeShaderCode;
                results.Add(toolChain.Compile(csCode, Stage.Compute, csFunction.Name));
            }

            // Collate results
            StringBuilder builder = new StringBuilder();

            foreach (CompileResult result in results)
            {
                if (result.HasError)
                {
                    builder.AppendLine(result.ToString());
                }
            }

            Assert.True(builder.Length < 1, builder.ToString());
        }
Beispiel #7
0
        private static bool CompileCode(LanguageBackend lang, string shaderPath, string entryPoint, ShaderFunctionType type, out string path)
        {
            Type langType = lang.GetType();

            if (langType == typeof(HlslBackend) && IsFxcAvailable())
            {
                return(CompileHlsl(shaderPath, entryPoint, type, out path));
            }
            else if (langType == typeof(Glsl450Backend) && IsGlslangValidatorAvailable())
            {
                return(CompileSpirv(shaderPath, entryPoint, type, out path));
            }
            else
            {
                path = null;
                return(false);
            }
        }
Beispiel #8
0
        public void PartialFiles()
        {
            ToolChain toolChain = ToolChain.Get(ToolFeatures.ToCompiled);

            if (toolChain == null)
            {
                throw new RequiredToolFeatureMissingException("No tool chain supporting compilation was found!");
            }

            Compilation     compilation = TestUtil.GetCompilation();
            LanguageBackend backend     = toolChain.CreateBackend(compilation);
            ShaderGenerator sg          = new ShaderGenerator(compilation, backend, "TestShaders.PartialVertex.VertexShaderFunc");

            ShaderGenerationResult             genResult = sg.GenerateShaders();
            IReadOnlyList <GeneratedShaderSet> sets      = genResult.GetOutput(backend);

            Assert.Equal(1, sets.Count);
            GeneratedShaderSet set         = sets[0];
            ShaderModel        shaderModel = set.Model;
            string             vsCode      = set.VertexShaderCode;
            CompileResult      result      = toolChain.Compile(vsCode, Stage.Vertex, "VertexShaderFunc");

            Assert.False(result.HasError, result.ToString());
        }
Beispiel #9
0
 public MetalMethodVisitor(Compilation compilation, string setName, ShaderFunction shaderFunction, LanguageBackend backend)
     : base(compilation, setName, shaderFunction, backend)
 {
 }
Beispiel #10
0
        public static int Main(string[] args)
        {
            string referenceItemsResponsePath = null;
            string compileItemsResponsePath   = null;
            string outputPath      = null;
            string genListFilePath = null;
            bool   listAllFiles    = false;
            string processorPath   = null;
            string processorArgs   = null;

            for (int i = 0; i < args.Length; i++)
            {
                args[i] = args[i].Replace("\\\\", "\\");
            }

            ArgumentSyntax.Parse(args, syntax =>
            {
                syntax.DefineOption("ref", ref referenceItemsResponsePath, true, "The semicolon-separated list of references to compile against.");
                syntax.DefineOption("src", ref compileItemsResponsePath, true, "The semicolon-separated list of source files to compile.");
                syntax.DefineOption("out", ref outputPath, true, "The output path for the generated shaders.");
                syntax.DefineOption("genlist", ref genListFilePath, true, "The output file to store the list of generated files.");
                syntax.DefineOption("listall", ref listAllFiles, false, "Forces all generated files to be listed in the list file. By default, only bytecode files will be listed and not the original shader code.");
                syntax.DefineOption("processor", ref processorPath, false, "The path of an assembly containing IShaderSetProcessor types to be used to post-process GeneratedShaderSet objects.");
                syntax.DefineOption("processorargs", ref processorArgs, false, "Custom information passed to IShaderSetProcessor.");
            });

            referenceItemsResponsePath = NormalizePath(referenceItemsResponsePath);
            compileItemsResponsePath   = NormalizePath(compileItemsResponsePath);
            outputPath      = NormalizePath(outputPath);
            genListFilePath = NormalizePath(genListFilePath);
            processorPath   = NormalizePath(processorPath);

            if (!File.Exists(referenceItemsResponsePath))
            {
                Console.Error.WriteLine("Reference items response file does not exist: " + referenceItemsResponsePath);
                return(-1);
            }
            if (!File.Exists(compileItemsResponsePath))
            {
                Console.Error.WriteLine("Compile items response file does not exist: " + compileItemsResponsePath);
                return(-1);
            }
            if (!Directory.Exists(outputPath))
            {
                try
                {
                    Directory.CreateDirectory(outputPath);
                }
                catch
                {
                    Console.Error.WriteLine($"Unable to create the output directory \"{outputPath}\".");
                    return(-1);
                }
            }

            string[] referenceItems = File.ReadAllLines(referenceItemsResponsePath);
            string[] compileItems   = File.ReadAllLines(compileItemsResponsePath);

            List <MetadataReference> references = new List <MetadataReference>();

            foreach (string referencePath in referenceItems)
            {
                if (!File.Exists(referencePath))
                {
                    Console.Error.WriteLine("Error: reference does not exist: " + referencePath);
                    return(1);
                }

                using (FileStream fs = File.OpenRead(referencePath))
                {
                    references.Add(MetadataReference.CreateFromStream(fs, filePath: referencePath));
                }
            }

            List <SyntaxTree> syntaxTrees = new List <SyntaxTree>();

            foreach (string sourcePath in compileItems)
            {
                string fullSourcePath = Path.Combine(Environment.CurrentDirectory, sourcePath);
                if (!File.Exists(fullSourcePath))
                {
                    Console.Error.WriteLine("Error: source file does not exist: " + fullSourcePath);
                    return(1);
                }

                using (FileStream fs = File.OpenRead(fullSourcePath))
                {
                    SourceText text = SourceText.From(fs);
                    syntaxTrees.Add(CSharpSyntaxTree.ParseText(text, path: fullSourcePath));
                }
            }

            Compilation compilation = CSharpCompilation.Create(
                "ShaderGen.App.GenerateShaders",
                syntaxTrees,
                references,
                options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            HlslBackend      hlsl      = new HlslBackend(compilation);
            Glsl330Backend   glsl330   = new Glsl330Backend(compilation);
            GlslEs300Backend glsles300 = new GlslEs300Backend(compilation);
            Glsl450Backend   glsl450   = new Glsl450Backend(compilation);
            MetalBackend     metal     = new MetalBackend(compilation);

            LanguageBackend[] languages = new LanguageBackend[]
            {
                hlsl,
                glsl330,
                glsles300,
                glsl450,
                metal,
            };

            List <IShaderSetProcessor> processors = new List <IShaderSetProcessor>();

            if (processorPath != null)
            {
                try
                {
                    Assembly           assm           = Assembly.LoadFrom(processorPath);
                    IEnumerable <Type> processorTypes = assm.GetTypes().Where(
                        t => t.GetInterface(nameof(ShaderGen) + "." + nameof(IShaderSetProcessor)) != null);
                    foreach (Type type in processorTypes)
                    {
                        IShaderSetProcessor processor = (IShaderSetProcessor)Activator.CreateInstance(type);
                        processor.UserArgs = processorArgs;
                        processors.Add(processor);
                    }
                }
                catch (ReflectionTypeLoadException rtle)
                {
                    string msg = string.Join(Environment.NewLine, rtle.LoaderExceptions.Select(e => e.ToString()));
                    Console.WriteLine("FAIL: " + msg);
                    throw new Exception(msg);
                }
            }

            ShaderGenerator        sg = new ShaderGenerator(compilation, languages, processors.ToArray());
            ShaderGenerationResult shaderGenResult;

            try
            {
                shaderGenResult = sg.GenerateShaders();
            }
            catch (Exception e) when(!Debugger.IsAttached)
            {
                StringBuilder sb = new StringBuilder();

                sb.AppendLine("An error was encountered while generating shader code:");
                sb.AppendLine(e.ToString());
                Console.Error.WriteLine(sb.ToString());
                return(-1);
            }

            Encoding      outputEncoding     = new UTF8Encoding(false);
            List <string> generatedFilePaths = new List <string>();

            foreach (LanguageBackend lang in languages)
            {
                string extension = BackendExtension(lang);
                IReadOnlyList <GeneratedShaderSet> sets = shaderGenResult.GetOutput(lang);
                foreach (GeneratedShaderSet set in sets)
                {
                    string name = set.Name;
                    if (set.VertexShaderCode != null)
                    {
                        string vsOutName = name + "-vertex." + extension;
                        string vsOutPath = Path.Combine(outputPath, vsOutName);
                        File.WriteAllText(vsOutPath, set.VertexShaderCode, outputEncoding);
                        bool succeeded = CompileCode(
                            lang,
                            vsOutPath,
                            set.VertexFunction.Name,
                            ShaderFunctionType.VertexEntryPoint,
                            out string[] genPaths);
                        if (succeeded)
                        {
                            generatedFilePaths.AddRange(genPaths);
                        }
                        if (!succeeded || listAllFiles)
                        {
                            generatedFilePaths.Add(vsOutPath);
                        }
                    }
                    if (set.FragmentShaderCode != null)
                    {
                        string fsOutName = name + "-fragment." + extension;
                        string fsOutPath = Path.Combine(outputPath, fsOutName);
                        File.WriteAllText(fsOutPath, set.FragmentShaderCode, outputEncoding);
                        bool succeeded = CompileCode(
                            lang,
                            fsOutPath,
                            set.FragmentFunction.Name,
                            ShaderFunctionType.FragmentEntryPoint,
                            out string[] genPaths);
                        if (succeeded)
                        {
                            generatedFilePaths.AddRange(genPaths);
                        }
                        if (!succeeded || listAllFiles)
                        {
                            generatedFilePaths.Add(fsOutPath);
                        }
                    }
                    if (set.ComputeShaderCode != null)
                    {
                        string csOutName = name + "-compute." + extension;
                        string csOutPath = Path.Combine(outputPath, csOutName);
                        File.WriteAllText(csOutPath, set.ComputeShaderCode, outputEncoding);
                        bool succeeded = CompileCode(
                            lang,
                            csOutPath,
                            set.ComputeFunction.Name,
                            ShaderFunctionType.ComputeEntryPoint,
                            out string[] genPaths);
                        if (succeeded)
                        {
                            generatedFilePaths.AddRange(genPaths);
                        }
                        if (!succeeded || listAllFiles)
                        {
                            generatedFilePaths.Add(csOutPath);
                        }
                    }
                }
            }

            File.WriteAllLines(genListFilePath, generatedFilePaths);

            return(0);
        }
Beispiel #11
0
 /// <summary>
 /// Gets the <see cref="ToolChain" /> for the specified backend.
 /// </summary>
 /// <param name="backend">The backend.</param>
 /// <returns>
 /// A <see cref="ToolChain" /> if available; otherwise <see langword="null" />.
 /// </returns>
 public static ToolChain Get(LanguageBackend backend) => backend != null?Get(backend.GetType()) : null;