/// <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 });
        }
Exemple #3
0
        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);
        }
Exemple #4
0
        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);
                }
            }
        }
Exemple #6
0
        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());
        }