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); }
/// <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); }
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); } } } } }
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()); }
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); } }
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()); }
public MetalMethodVisitor(Compilation compilation, string setName, ShaderFunction shaderFunction, LanguageBackend backend) : base(compilation, setName, shaderFunction, backend) { }
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); }
/// <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;