diff --git a/ext/update_shaderc_sources.py b/ext/update_shaderc_sources.py index 3c4a54e..cfe8adf 100644 --- a/ext/update_shaderc_sources.py +++ b/ext/update_shaderc_sources.py @@ -1,5 +1,4 @@ #!/usr/bin/env python - # Copyright 2016 The Shaderc Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,33 +12,23 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -"""Get source files for Shaderc and its dependencies from public repositories. -""" - -from __future__ import print_function - +"""Get source files for Shaderc and its dependencies from public +repositories.""" +from operator import attrgetter import argparse import json import distutils.dir_util import os.path import subprocess import sys - KNOWN_GOOD_FILE = 'known_good.json' - # Maps a site name to its hostname. -SITE_TO_HOST = { 'github' : 'github.com', - 'gitlab' : 'gitlab.com'} - +SITE_TO_HOST = {'github': 'github.com', + 'gitlab': 'gitlab.com'} VERBOSE = True - - def command_output(cmd, directory, fail_ok=False): """Runs a command in a directory and returns its standard output stream. - Captures the standard error stream. - Raises a RuntimeError if the command fails to launch or otherwise fails. """ if VERBOSE: @@ -53,14 +42,10 @@ def command_output(cmd, directory, fail_ok=False): if VERBOSE: print(stdout) return stdout - - class GoodCommit(object): """Represents a good commit for a repository.""" - def __init__(self, json): """Initializes this good commit object. - Args: 'json': A fully populated JSON object describing the commit. """ @@ -70,72 +55,58 @@ def __init__(self, json): self.subrepo = json['subrepo'] self.subdir = json['subdir'] if ('subdir' in json) else '.' self.commit = json['commit'] - def GetUrl(self, style='https'): """Returns the URL for the repository.""" host = SITE_TO_HOST[self.site] sep = '/' if (style is 'https') else ':' return '{style}://{host}{sep}{subrepo}'.format( - style=style, - host=host, - sep=sep, - subrepo=self.subrepo) - + style=style, + host=host, + sep=sep, + subrepo=self.subrepo) def AddRemote(self): """Add the remote 'known-good' if it does not exist.""" if len(command_output(['git', 'remote', 'get-url', 'known-good'], self.subdir, fail_ok=True)) == 0: - command_output(['git', 'remote', 'add', 'known-good', self.GetUrl()], self.subdir) - + command_output( + ['git', 'remote', 'add', 'known-good', self.GetUrl()], self.subdir) def HasCommit(self): """Check if the repository contains the known-good commit.""" return 0 == subprocess.call(['git', 'rev-parse', '--verify', '--quiet', - self.commit + "^{commit}"], + self.commit + '^{commit}'], cwd=self.subdir) - def Clone(self): distutils.dir_util.mkpath(self.subdir) command_output(['git', 'clone', self.GetUrl(), '.'], self.subdir) - def Fetch(self): command_output(['git', 'fetch', 'known-good'], self.subdir) - def Checkout(self): - if not os.path.exists(os.path.join(self.subdir,'.git')): + if not os.path.exists(os.path.join(self.subdir, '.git')): self.Clone() self.AddRemote() if not self.HasCommit(): self.Fetch() command_output(['git', 'checkout', self.commit], self.subdir) - - def GetGoodCommits(known_good_file): """Returns the latest list of GoodCommit objects.""" with open(known_good_file) as known_good: return [GoodCommit(c) for c in json.loads(known_good.read())['commits']] - - def main(): - parser = argparse.ArgumentParser(description='Get Shaderc source dependencies at a known-good commit') + parser = argparse.ArgumentParser( + description='Get Shaderc source dependencies at a known-good commit') parser.add_argument('--dir', dest='dir', default='src', help="Set target directory for Shaderc source root. Default is \'src\'.") parser.add_argument('--file', dest='known_good_file', default=KNOWN_GOOD_FILE, help="The file containing known-good commits. Default is \'' + KNOWN_GOOD_FILE + '\'.") - args = parser.parse_args() - commits = GetGoodCommits(args.known_good_file) - distutils.dir_util.mkpath(args.dir) print('Change directory to {d}'.format(d=args.dir)) os.chdir(args.dir) - # Create the subdirectories in sorted order so that parent git repositories # are created first. - for c in sorted(commits, cmp=lambda x,y: cmp(x.subdir, y.subdir)): + for c in sorted(commits, key=attrgetter('subdir')): print('Get {n}\n'.format(n=c.name)) c.Checkout() sys.exit(0) - - if __name__ == '__main__': main() \ No newline at end of file diff --git a/src/Veldrid.SPIRV.Tests/CompilationTests.cs b/src/Veldrid.SPIRV.Tests/CompilationTests.cs index c44ba9d..e261419 100644 --- a/src/Veldrid.SPIRV.Tests/CompilationTests.cs +++ b/src/Veldrid.SPIRV.Tests/CompilationTests.cs @@ -99,6 +99,22 @@ public void ComputeSucceeds(string cs, CrossCompileTarget target) Assert.NotNull(result.ComputeShader); } + [Theory] + //[InlineData("empty.geom", CrossCompileTarget.HLSL)] + [InlineData("empty.geom", CrossCompileTarget.GLSL)] + [InlineData("empty.geom", CrossCompileTarget.ESSL)] + [InlineData("empty.geom", CrossCompileTarget.MSL)] + //[InlineData("simple.geom", CrossCompileTarget.HLSL)] + [InlineData("simple.geom", CrossCompileTarget.GLSL)] + [InlineData("simple.geom", CrossCompileTarget.ESSL)] + [InlineData("simple.geom", CrossCompileTarget.MSL)] + public void GeometrySucceeds(string cs, CrossCompileTarget target) + { + byte[] csBytes = TestUtil.LoadBytes(cs); + ComputeCompilationResult result = SpirvCompilation.CompileGeometry(csBytes, target); + Assert.NotNull(result.ComputeShader); + } + [Theory] [InlineData("overlapping-resources.vert.spv", "overlapping-resources.frag.spv", CrossCompileTarget.HLSL)] [InlineData("overlapping-resources.vert", "overlapping-resources.frag.spv", CrossCompileTarget.HLSL)] diff --git a/src/Veldrid.SPIRV.Tests/TestShaders/empty.geom b/src/Veldrid.SPIRV.Tests/TestShaders/empty.geom new file mode 100644 index 0000000..1a15d7a --- /dev/null +++ b/src/Veldrid.SPIRV.Tests/TestShaders/empty.geom @@ -0,0 +1,9 @@ +#version 450 + +layout(triangles) in; +layout(triangle_strip, max_vertices=3) out; + +void main() +{ + EndPrimitive(); +} \ No newline at end of file diff --git a/src/Veldrid.SPIRV.Tests/TestShaders/simple.geom b/src/Veldrid.SPIRV.Tests/TestShaders/simple.geom new file mode 100644 index 0000000..777fb47 --- /dev/null +++ b/src/Veldrid.SPIRV.Tests/TestShaders/simple.geom @@ -0,0 +1,14 @@ +#version 400 +layout(triangles) in; +layout(triangle_strip, max_vertices = 3) out; + +void main() +{ + for (int i = 0; i + netcoreapp2.0 @@ -22,6 +22,8 @@ + + diff --git a/src/Veldrid.SPIRV.VariantCompiler/CompilationSet.cs b/src/Veldrid.SPIRV.VariantCompiler/CompilationSet.cs index 6205518..e9e1ed0 100644 --- a/src/Veldrid.SPIRV.VariantCompiler/CompilationSet.cs +++ b/src/Veldrid.SPIRV.VariantCompiler/CompilationSet.cs @@ -59,6 +59,7 @@ public string[] Compile(ShaderVariantDescription variant) { if (variant.Shaders[0].Stage == ShaderStages.Vertex) { return CompileVertexFragment(variant); } if (variant.Shaders[0].Stage == ShaderStages.Compute) { return CompileCompute(variant); } + if (variant.Shaders[0].Stage == ShaderStages.Geometry) { return CompileGeometry(variant); } } if (variant.Shaders.Length == 2) { @@ -285,5 +286,50 @@ private string[] CompileCompute(ShaderVariantDescription variant) return generatedFiles.ToArray(); } + + private string[] CompileGeometry(ShaderVariantDescription variant) + { + List generatedFiles = new List(); + byte[] csBytes = CompileToSpirv(variant, variant.Shaders[0].FileName, ShaderStages.Geometry); + string spvPath = Path.Combine(_outputPath, $"{variant.Name}_{ShaderStages.Geometry.ToString()}.spv"); + File.WriteAllBytes(spvPath, csBytes); + generatedFiles.Add(spvPath); + + List compilationExceptions = new List(); + foreach (CrossCompileTarget target in variant.Targets) + { + try + { + GeometryCompilationResult result = SpirvCompilation.CompileGeometry(csBytes, target, variant.CrossCompileOptions); + string csPath = Path.Combine(_outputPath, $"{variant.Name}_Geometry.{GetExtension(target)}"); + File.WriteAllText(csPath, result.GeometryShader); + generatedFiles.Add(csPath); + + string reflectionPath = Path.Combine(_outputPath, $"{variant.Name}_ReflectionInfo.json"); + + JsonSerializer serializer = new JsonSerializer(); + serializer.Formatting = Formatting.Indented; + StringEnumConverter enumConverter = new StringEnumConverter(); + serializer.Converters.Add(enumConverter); + using (StreamWriter sw = File.CreateText(reflectionPath)) + using (JsonTextWriter jtw = new JsonTextWriter(sw)) + { + serializer.Serialize(jtw, result.Reflection); + } + generatedFiles.Add(reflectionPath); + } + catch (Exception e) + { + compilationExceptions.Add(e); + } + } + + if (compilationExceptions.Count > 0) + { + throw new AggregateException($"Errors were encountered when compiling shader variant(s).", compilationExceptions); + } + + return generatedFiles.ToArray(); + } } } diff --git a/src/Veldrid.SPIRV/CrossCompileInfo.cs b/src/Veldrid.SPIRV/CrossCompileInfo.cs index f9313d7..fa13b1b 100644 --- a/src/Veldrid.SPIRV/CrossCompileInfo.cs +++ b/src/Veldrid.SPIRV/CrossCompileInfo.cs @@ -12,5 +12,6 @@ internal struct CrossCompileInfo public InteropArray VertexShader; public InteropArray FragmentShader; public InteropArray ComputeShader; + public InteropArray GeometryShader; } } diff --git a/src/Veldrid.SPIRV/SpirvCompilation.cs b/src/Veldrid.SPIRV/SpirvCompilation.cs index b7ddfa4..b0f95a0 100644 --- a/src/Veldrid.SPIRV/SpirvCompilation.cs +++ b/src/Veldrid.SPIRV/SpirvCompilation.cs @@ -161,7 +161,7 @@ ref NativeVertexElementDescription nativeDesc } /// - /// Cross-compiles the given vertex-fragment pair into some target language. + /// Cross-compiles the given compute shader into some target language. /// /// The compute shader's SPIR-V bytecode or ASCII-encoded GLSL source code. /// The target language. @@ -171,7 +171,7 @@ public static unsafe ComputeCompilationResult CompileCompute( CrossCompileTarget target) => CompileCompute(csBytes, target, new CrossCompileOptions()); /// - /// Cross-compiles the given vertex-fragment pair into some target language. + /// Cross-compiles the given compute shader into some target language. /// /// The compute shader's SPIR-V bytecode or ASCII-encoded GLSL source code. /// The target language. @@ -262,6 +262,110 @@ public static unsafe ComputeCompilationResult CompileCompute( } } + /// + /// Cross-compiles the given geometry sgader into some target language. + /// + /// The geometry shader's SPIR-V bytecode or ASCII-encoded GLSL source code. + /// The target language. + /// A containing the compiled output. + public static unsafe ComputeCompilationResult CompileGeometry( + byte[] csBytes, + CrossCompileTarget target) => CompileGeometry(csBytes, target, new CrossCompileOptions()); + + + /// + /// Cross-compiles the given geometry shader into some target language. + /// + /// The geometry shader's SPIR-V bytecode or ASCII-encoded GLSL source code. + /// The target language. + /// The options for shader translation. + /// A containing the compiled output. + public static unsafe ComputeCompilationResult CompileGeometry( + byte[] csBytes, + CrossCompileTarget target, + CrossCompileOptions options) + { + byte[] csSpirvBytes; + + if (Util.HasSpirvHeader(csBytes)) + { + csSpirvBytes = csBytes; + } + else + { + fixed (byte* sourceTextPtr = csBytes) + { + SpirvCompilationResult vsCompileResult = CompileGlslToSpirv( + (uint)csBytes.Length, + sourceTextPtr, + string.Empty, + ShaderStages.Geometry, + target == CrossCompileTarget.GLSL || target == CrossCompileTarget.ESSL, + 0, + null); + csSpirvBytes = vsCompileResult.SpirvBytes; + } + } + + CrossCompileInfo info; + info.Target = target; + info.FixClipSpaceZ = options.FixClipSpaceZ; + info.InvertY = options.InvertVertexOutputY; + fixed (byte* csBytesPtr = csSpirvBytes) + fixed (SpecializationConstant* specConstants = options.Specializations) + { + info.GeometryShader = new InteropArray((uint)csSpirvBytes.Length / 4, csBytesPtr); + info.Specializations = new InteropArray((uint)options.Specializations.Length, specConstants); + + CompilationResult* result = null; + try + { + result = VeldridSpirvNative.CrossCompile(&info); + if (!result->Succeeded) + { + throw new SpirvCompilationException( + "Compilation failed: " + Util.GetString((byte*)result->GetData(0), result->GetLength(0))); + } + + string csCode = Util.GetString((byte*)result->GetData(0), result->GetLength(0)); + + ReflectionInfo* reflInfo = &result->ReflectionInfo; + + ResourceLayoutDescription[] layouts = new ResourceLayoutDescription[reflInfo->ResourceLayouts.Count]; + for (uint i = 0; i < reflInfo->ResourceLayouts.Count; i++) + { + ref NativeResourceLayoutDescription nativeDesc = + ref reflInfo->ResourceLayouts.Ref(i); + layouts[i].Elements = new ResourceLayoutElementDescription[nativeDesc.ResourceElements.Count]; + for (uint j = 0; j < nativeDesc.ResourceElements.Count; j++) + { + ref NativeResourceElementDescription elemDesc = + ref nativeDesc.ResourceElements.Ref(j); + layouts[i].Elements[j] = new ResourceLayoutElementDescription( + Util.GetString((byte*)elemDesc.Name.Data, elemDesc.Name.Count), + elemDesc.Kind, + elemDesc.Stages, + elemDesc.Options); + } + } + + SpirvReflection reflection = new SpirvReflection( + Array.Empty(), + layouts); + + return new ComputeCompilationResult(csCode, reflection); + } + finally + { + if (result != null) + { + VeldridSpirvNative.FreeResult(result); + } + } + } + } + + /// /// Compiles the given GLSL source code into SPIR-V. /// diff --git a/src/libveldrid-spirv/InteropStructs.hpp b/src/libveldrid-spirv/InteropStructs.hpp index 92d90d8..e1f221c 100644 --- a/src/libveldrid-spirv/InteropStructs.hpp +++ b/src/libveldrid-spirv/InteropStructs.hpp @@ -128,6 +128,7 @@ struct CrossCompileInfo InteropArray VertexShader; InteropArray FragmentShader; InteropArray ComputeShader; + InteropArray GeometryShader; }; #pragma pack(pop) diff --git a/src/libveldrid-spirv/libveldrid-spirv.cpp b/src/libveldrid-spirv/libveldrid-spirv.cpp index 01d92d1..9376493 100644 --- a/src/libveldrid-spirv/libveldrid-spirv.cpp +++ b/src/libveldrid-spirv/libveldrid-spirv.cpp @@ -472,11 +472,11 @@ CompilationResult *CompileVertexFragment(const CrossCompileInfo &info) return result; } -CompilationResult *CompileCompute(const CrossCompileInfo &info) +CompilationResult *CompileComputeOrGeometry(const CrossCompileInfo &info, const InteropArray& shaderData) { std::vector csBytes( - info.ComputeShader.Data, - info.ComputeShader.Data + info.ComputeShader.Count); + shaderData.Data, + shaderData.Data + shaderData.Count); Compiler *csCompiler = GetCompiler(csBytes, info); SetSpecializations(csCompiler, info); @@ -555,6 +555,8 @@ CompilationResult *CompileCompute(const CrossCompileInfo &info) return result; } + + CompilationResult *Compile(const CrossCompileInfo &info) { if (info.VertexShader.Count > 0 && info.FragmentShader.Count > 0) @@ -563,7 +565,11 @@ CompilationResult *Compile(const CrossCompileInfo &info) } else if (info.ComputeShader.Count > 0) { - return CompileCompute(info); + return CompileComputeOrGeometry(info, info.ComputeShader); + } + else if (info.GeometryShader.Count > 0) + { + return CompileComputeOrGeometry(info, info.GeometryShader); } return new CompilationResult("The given combination of shaders was not valid.");