/// <summary> /// Creates a compute shader from the given <see cref="ShaderDescription"/> containing SPIR-V bytecode or GLSL source /// code. /// </summary> /// <param name="factory">The <see cref="ResourceFactory"/> used to compile the translated shader code.</param> /// <param name="computeShaderDescription">The compute shader's description. /// <see cref="ShaderDescription.ShaderBytes"/> should contain SPIR-V bytecode or Vulkan-style GLSL source code which /// can be compiled to SPIR-V.</param> /// <param name="options">The <see cref="CrossCompileOptions"/> which will control the parameters used to translate the /// shaders from SPIR-V to the target language.</param> /// <returns>The compiled compute <see cref="Shader"/>.</returns> public static Shader CreateFromSpirv( this ResourceFactory factory, ShaderDescription computeShaderDescription, CrossCompileOptions options) { GraphicsBackend backend = factory.BackendType; if (backend == GraphicsBackend.Vulkan) { computeShaderDescription.ShaderBytes = EnsureSpirv(computeShaderDescription); return(factory.CreateShader(ref computeShaderDescription)); } CrossCompileTarget target = GetCompilationTarget(factory.BackendType); ComputeCompilationResult compilationResult = SpirvCompilation.CompileCompute( computeShaderDescription.ShaderBytes, target, options); string computeEntryPoint = (backend == GraphicsBackend.Metal && computeShaderDescription.EntryPoint == "main") ? "main0" : computeShaderDescription.EntryPoint; byte[] computeBytes = GetBytes(backend, compilationResult.ComputeShader); return(factory.CreateShader(new ShaderDescription( computeShaderDescription.Stage, computeBytes, computeEntryPoint))); }
/// <summary> /// Creates a vertex and fragment shader pair from the given <see cref="ShaderDescription"/> pair containing SPIR-V /// bytecode or GLSL source code. /// </summary> /// <param name="factory">The <see cref="ResourceFactory"/> used to compile the translated shader code.</param> /// <param name="vertexShaderDescription">The vertex shader's description. <see cref="ShaderDescription.ShaderBytes"/> /// should contain SPIR-V bytecode or Vulkan-style GLSL source code which can be compiled to SPIR-V.</param> /// <param name="fragmentShaderDescription">The fragment shader's description. /// <see cref="ShaderDescription.ShaderBytes"/> should contain SPIR-V bytecode or Vulkan-style GLSL source code which /// can be compiled to SPIR-V.</param> /// <param name="options">The <see cref="CrossCompileOptions"/> which will control the parameters used to translate the /// shaders from SPIR-V to the target language.</param> /// <returns>A two-element array, containing the vertex shader (element 0) and the fragment shader (element 1).</returns> public static Shader[] CreateFromSpirv( this ResourceFactory factory, ShaderDescription vertexShaderDescription, ShaderDescription fragmentShaderDescription, CrossCompileOptions options) { GraphicsBackend backend = factory.BackendType; if (backend == GraphicsBackend.Vulkan) { vertexShaderDescription.ShaderBytes = EnsureSpirv(vertexShaderDescription); fragmentShaderDescription.ShaderBytes = EnsureSpirv(fragmentShaderDescription); return(new Shader[] { factory.CreateShader(ref vertexShaderDescription), factory.CreateShader(ref fragmentShaderDescription) }); } CrossCompileTarget target = GetCompilationTarget(factory.BackendType); VertexFragmentCompilationResult compilationResult = SpirvCompilation.CompileVertexFragment( vertexShaderDescription.ShaderBytes, fragmentShaderDescription.ShaderBytes, target, options); string vertexEntryPoint = (backend == GraphicsBackend.Metal && vertexShaderDescription.EntryPoint == "main") ? "main0" : vertexShaderDescription.EntryPoint; byte[] vertexBytes = GetBytes(backend, compilationResult.VertexShader); Shader vertexShader = factory.CreateShader(new ShaderDescription( vertexShaderDescription.Stage, vertexBytes, vertexEntryPoint)); string fragmentEntryPoint = (backend == GraphicsBackend.Metal && fragmentShaderDescription.EntryPoint == "main") ? "main0" : fragmentShaderDescription.EntryPoint; byte[] fragmentBytes = GetBytes(backend, compilationResult.FragmentShader); Shader fragmentShader = factory.CreateShader(new ShaderDescription( fragmentShaderDescription.Stage, fragmentBytes, fragmentEntryPoint)); return(new Shader[] { vertexShader, fragmentShader }); }
private byte[] CompileToSpirv( ShaderVariantDescription variant, string fileName, ShaderStages stage) { GlslCompileOptions glslOptions = GetOptions(variant); string glsl = LoadGlsl(fileName); SpirvCompilationResult result = SpirvCompilation.CompileGlslToSpirv( glsl, fileName, stage, glslOptions); return(result.SpirvBytes); }
private string[] CompileCompute(ShaderVariantDescription variant) { List <string> generatedFiles = new List <string>(); byte[] csBytes = CompileToSpirv(variant, variant.Shaders[0].FileName, ShaderStages.Compute); string spvPath = Path.Combine(_outputPath, $"{variant.Name}_{ShaderStages.Compute.ToString()}.spv"); File.WriteAllBytes(spvPath, csBytes); generatedFiles.Add(spvPath); List <Exception> compilationExceptions = new List <Exception>(); foreach (CrossCompileTarget target in variant.Targets) { try { ComputeCompilationResult result = SpirvCompilation.CompileCompute(csBytes, target, variant.CrossCompileOptions); string csPath = Path.Combine(_outputPath, $"{variant.Name}_Compute.{GetExtension(target)}"); File.WriteAllText(csPath, result.ComputeShader); 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()); }
private static unsafe byte[] EnsureSpirv(ShaderDescription description) { if (Util.HasSpirvHeader(description.ShaderBytes)) { return(description.ShaderBytes); } else { fixed(byte *sourceAsciiPtr = description.ShaderBytes) { SpirvCompilationResult glslCompileResult = SpirvCompilation.CompileGlslToSpirv( (uint)description.ShaderBytes.Length, sourceAsciiPtr, null, description.Stage, description.Debug, 0, null); return(glslCompileResult.SpirvBytes); } } }
private string[] CompileVertexFragment(ShaderVariantDescription variant) { List <string> generatedFiles = new List <string>(); List <Exception> compilationExceptions = new List <Exception>(); byte[] vsBytes = null; byte[] fsBytes = null; string vertexFileName = variant.Shaders.FirstOrDefault(vsd => vsd.Stage == ShaderStages.Vertex)?.FileName; if (vertexFileName != null) { try { vsBytes = CompileToSpirv(variant, vertexFileName, ShaderStages.Vertex); //string spvPath = Path.Combine(_outputPath, $"{variant.Name}_{ShaderStages.Vertex.ToString()}.spv"); string spvPath = Path.Combine(_outputPath, $"{variant.Name}.vert.spv"); File.WriteAllBytes(spvPath, vsBytes); generatedFiles.Add(spvPath); } catch (Exception e) { compilationExceptions.Add(e); } } string fragmentFileName = variant.Shaders.FirstOrDefault(vsd => vsd.Stage == ShaderStages.Fragment)?.FileName; if (fragmentFileName != null) { try { fsBytes = CompileToSpirv(variant, fragmentFileName, ShaderStages.Fragment); //string spvPath = Path.Combine(_outputPath, $"{variant.Name}_{ShaderStages.Fragment.ToString()}.spv"); string spvPath = Path.Combine(_outputPath, $"{variant.Name}.frag.spv"); File.WriteAllBytes(spvPath, fsBytes); generatedFiles.Add(spvPath); } catch (Exception e) { compilationExceptions.Add(e); } } if (compilationExceptions.Count > 0) { throw new AggregateException( $"Errors were encountered when compiling from GLSL to SPIR-V.", compilationExceptions); } foreach (CrossCompileTarget target in variant.Targets) { try { bool writeReflectionFile = true; VertexFragmentCompilationResult result = SpirvCompilation.CompileVertexFragment( vsBytes, fsBytes, target, variant.CrossCompileOptions); if (result.VertexShader != null) { string vsPath = Path.Combine(_outputPath, $"{variant.Name}_Vertex.{GetExtension(target)}"); File.WriteAllText(vsPath, result.VertexShader); generatedFiles.Add(vsPath); } if (result.FragmentShader != null) { string fsPath = Path.Combine(_outputPath, $"{variant.Name}_Fragment.{GetExtension(target)}"); File.WriteAllText(fsPath, result.FragmentShader); generatedFiles.Add(fsPath); } if (writeReflectionFile) { writeReflectionFile = false; 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()); }