public static bool Optimize(CompileOptions options, string inFile, string outFile, out CompileError error)
        {
            if (!Initialize(out var initError))
            {
                error = new CompileError(ErrorSource.Compiler, 0, 0, initError);
                return(false);
            }

            // Describe the process
            ProcessStartInfo psi = new ProcessStartInfo {
                FileName               = $"\"{TOOL_PATH}\"",
                Arguments              = $"-O --strip-debug \"{inFile}\" -o \"{outFile}\"",
                UseShellExecute        = false,
                CreateNoWindow         = true,
                RedirectStandardError  = true,
                RedirectStandardOutput = true,
                WindowStyle            = ProcessWindowStyle.Hidden,
                ErrorDialog            = false
            };

            // Run the optimizer
            string stdout = null;

            using (Process proc = new Process())
            {
                proc.StartInfo = psi;
                proc.Start();
                bool done = proc.WaitForExit((int)options.CompilerTimeout);
                if (!done)
                {
                    proc.Kill();
                    error = new CompileError(ErrorSource.Compiler, 0, 0, "Optimizer process timed out.");
                    return(false);
                }
                stdout = proc.StandardOutput.ReadToEnd() + '\n' + proc.StandardError.ReadToEnd();
            }

            // Convert the output to a list of error messages
            var lines = stdout
                        .Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
                        .Where(line => line.StartsWith("error:"))
                        .Select(line => line.Trim().Substring(7))         // Trim the "error: " text off of the front
                        .ToList();

            // Report the errors, if present
            if (lines.Count > 0)
            {
                error = new CompileError(ErrorSource.Compiler, 0, 0, String.Join("\n", lines));
                return(false);
            }

            error = null;
            return(true);
        }
예제 #2
0
        private bool compile(CompileOptions options, SSLVisitor visitor, out CompileError error)
        {
            var finalPath = options.OutputPath ?? CompileOptions.MakeDefaultOutputPath(SourceFile) ?? Path.Combine(Directory.GetCurrentDirectory(), "shader.spv");

            bool hasVert = (ShaderInfo.Stages & ShaderStages.Vertex) > 0,
                 hasTesc = (ShaderInfo.Stages & ShaderStages.TessControl) > 0,
                 hasTese = (ShaderInfo.Stages & ShaderStages.TessEval) > 0,
                 hasGeom = (ShaderInfo.Stages & ShaderStages.Geometry) > 0,
                 hasFrag = (ShaderInfo.Stages & ShaderStages.Fragment) > 0;

            string vertPath = null, tescPath = null, tesePath = null, geomPath = null, fragPath = null;

            if (hasVert && !GLSLV.Compile(options, visitor.GLSL.GetGLSLOutput(ShaderStages.Vertex), ShaderStages.Vertex, out vertPath, out error))
            {
                return(false);
            }
            if (hasTesc && !GLSLV.Compile(options, visitor.GLSL.GetGLSLOutput(ShaderStages.TessControl), ShaderStages.TessControl, out tescPath, out error))
            {
                return(false);
            }
            if (hasTese && !GLSLV.Compile(options, visitor.GLSL.GetGLSLOutput(ShaderStages.TessEval), ShaderStages.TessEval, out tesePath, out error))
            {
                return(false);
            }
            if (hasGeom && !GLSLV.Compile(options, visitor.GLSL.GetGLSLOutput(ShaderStages.Geometry), ShaderStages.Geometry, out geomPath, out error))
            {
                return(false);
            }
            if (hasFrag && !GLSLV.Compile(options, visitor.GLSL.GetGLSLOutput(ShaderStages.Fragment), ShaderStages.Fragment, out fragPath, out error))
            {
                return(false);
            }

            // Link the files
            var linkOut = options.OptimizeBytecode ? Path.GetTempFileName() : finalPath;

            if (!SPIRVLink.Link(options, new[] { vertPath, tescPath, tesePath, geomPath, fragPath }, linkOut, out error))
            {
                return(false);
            }

            // Optimize the files
            if (options.OptimizeBytecode && !SPIRVOpt.Optimize(options, linkOut, finalPath, out error))
            {
                return(false);
            }

            error = null;
            return(true);
        }
예제 #3
0
        private bool outputRefl(CompileOptions options, SSLVisitor vis, out CompileError error)
        {
            error = null;
            var rPath = options.ReflectionPath ?? CompileOptions.MakeDefaultReflectionPath(SourceFile) ?? Path.Combine(Directory.GetCurrentDirectory(), "shader.refl");

            try
            {
                vis.Info.SaveToFile(rPath, options.UseBinaryReflection);
            }
            catch (Exception e)
            {
                error = new CompileError(ErrorSource.Output, 0, 0, $"Unable to generate reflection info: {e.Message}.");
            }

            return(error == null);
        }
예제 #4
0
        // Submit some glsl source code for compilation
        public static bool Compile(CompileOptions options, string glsl, ShaderStages stage, out string bcFile, out CompileError error)
        {
            if (!Initialize(out string initError))
            {
                bcFile = null;
                error  = new CompileError(ErrorSource.Compiler, 0, 0, initError);
                return(false);
            }

            // Create the arguments
            var ep = ENTRY_NAMES[stage];

            bcFile = Path.GetTempFileName();
            var args = $"-V -e {ep} --sep {ep} --client vulkan100 -o \"{bcFile}\" --stdin -S {STAGE_NAMES[stage]}";

            // Define the process info
            ProcessStartInfo psi = new ProcessStartInfo {
                FileName               = $"\"{TOOL_PATH}\"",
                Arguments              = args,
                UseShellExecute        = false,
                CreateNoWindow         = true,
                RedirectStandardError  = true,
                RedirectStandardOutput = true,
                RedirectStandardInput  = true,
                WindowStyle            = ProcessWindowStyle.Hidden,
                ErrorDialog            = false
            };

            // Run the compiler
            string stdout = null;

            using (Process proc = new Process())
            {
                proc.StartInfo = psi;
                proc.Start();
                proc.StandardInput.AutoFlush = true;
                proc.StandardInput.Write(glsl);
                proc.StandardInput.Close();
                bool done = proc.WaitForExit((int)options.CompilerTimeout);
                if (!done)
                {
                    proc.Kill();
                    error = new CompileError(ErrorSource.Compiler, 0, 0, $"Compiler timed-out during stage '{stage}'.");
                    return(false);
                }
                stdout = proc.StandardOutput.ReadToEnd() + '\n' + proc.StandardError.ReadToEnd();
            }

            // Convert the output to a list of error messages
            var lines = stdout
                        .Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
                        .Where(line => line.StartsWith("ERROR:") && !line.Contains("Source entry point")) // Remove the error message about renaming the entry point
                        .Select(line => line.Trim().Substring(7))                                         // Trim the "ERROR: " text off of the front
                        .ToList();

            // Report the errors, if present
            if (lines.Count > 0)
            {
                error = new CompileError(ErrorSource.Compiler, 0, 0, String.Join("\n", lines));
                return(false);
            }

            error = null;
            return(true);
        }
        public static bool Link(CompileOptions options, string[] modules, string output, out CompileError error)
        {
            if (!Initialize(out string initError))
            {
                error = new CompileError(ErrorSource.Compiler, 0, 0, initError);
                return(false);
            }

            // Build the args
            StringBuilder args = new StringBuilder(512);

            args.Append($"--create-library --target-env vulkan1.0 --verify-ids");
            args.Append($" -o \"{output}\"");
            foreach (var mfile in modules)
            {
                if (mfile != null)
                {
                    args.Append($" \"{mfile}\"");
                }
            }

            // Describe the process
            ProcessStartInfo psi = new ProcessStartInfo {
                FileName               = $"\"{TOOL_PATH}\"",
                Arguments              = args.ToString(),
                UseShellExecute        = false,
                CreateNoWindow         = true,
                RedirectStandardError  = true,
                RedirectStandardOutput = true,
                WindowStyle            = ProcessWindowStyle.Hidden,
                ErrorDialog            = false
            };

            // Run the linker
            string stdout = null;

            using (Process proc = new Process())
            {
                proc.StartInfo = psi;
                proc.Start();
                bool done = proc.WaitForExit((int)options.CompilerTimeout);
                if (!done)
                {
                    proc.Kill();
                    error = new CompileError(ErrorSource.Compiler, 0, 0, "Linking process timed out.");
                    return(false);
                }
                stdout = proc.StandardOutput.ReadToEnd() + '\n' + proc.StandardError.ReadToEnd();
            }

            // Convert the output to a list of error messages
            var lines = stdout
                        .Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
                        .Where(line => line.StartsWith("error:"))
                        .Select(line => line.Trim().Substring(7))         // Trim the "error: " text off of the front
                        .ToList();

            // Report the errors, if present
            if (lines.Count > 0)
            {
                error = new CompileError(ErrorSource.Compiler, 0, 0, String.Join("\n", lines));
                return(false);
            }

            error = null;
            return(true);
        }
예제 #6
0
        private bool outputGLSL(CompileOptions options, SSLVisitor visitor, out CompileError error)
        {
            var outDir  = options.GLSLPath ?? CompileOptions.MakeDefaultGLSLPath(SourceFile) ?? Directory.GetCurrentDirectory();
            var outName = (SourceFile != null) ? Path.GetFileNameWithoutExtension(SourceFile) : "shader";

            if ((ShaderInfo.Stages & ShaderStages.Vertex) > 0)
            {
                try
                {
                    var path = Path.Combine(outDir, outName + ".vert");
                    if (File.Exists(path))
                    {
                        File.Delete(path);
                    }
                    File.WriteAllText(path, visitor.GLSL.GetGLSLOutput(ShaderStages.Vertex));
                }
                catch (Exception e)
                {
                    error = new CompileError(ErrorSource.Output, 0, 0,
                                             $"Unable to write vertex source file, reason: '{e.Message.Substring(0, e.Message.Length - 1)}'.");
                    return(false);
                }
            }
            if ((ShaderInfo.Stages & ShaderStages.TessControl) > 0)
            {
                try
                {
                    var path = Path.Combine(outDir, outName + ".tesc");
                    if (File.Exists(path))
                    {
                        File.Delete(path);
                    }
                    File.WriteAllText(path, visitor.GLSL.GetGLSLOutput(ShaderStages.TessControl));
                }
                catch (Exception e)
                {
                    error = new CompileError(ErrorSource.Output, 0, 0,
                                             $"Unable to write tess control source file, reason: '{e.Message.Substring(0, e.Message.Length - 1)}'.");
                    return(false);
                }
            }
            if ((ShaderInfo.Stages & ShaderStages.TessEval) > 0)
            {
                try
                {
                    var path = Path.Combine(outDir, outName + ".tese");
                    if (File.Exists(path))
                    {
                        File.Delete(path);
                    }
                    File.WriteAllText(path, visitor.GLSL.GetGLSLOutput(ShaderStages.TessEval));
                }
                catch (Exception e)
                {
                    error = new CompileError(ErrorSource.Output, 0, 0,
                                             $"Unable to write tess eval source file, reason: '{e.Message.Substring(0, e.Message.Length - 1)}'.");
                    return(false);
                }
            }
            if ((ShaderInfo.Stages & ShaderStages.Geometry) > 0)
            {
                try
                {
                    var path = Path.Combine(outDir, outName + ".geom");
                    if (File.Exists(path))
                    {
                        File.Delete(path);
                    }
                    File.WriteAllText(path, visitor.GLSL.GetGLSLOutput(ShaderStages.Geometry));
                }
                catch (Exception e)
                {
                    error = new CompileError(ErrorSource.Output, 0, 0,
                                             $"Unable to write geometry source file, reason: '{e.Message.Substring(0, e.Message.Length - 1)}'.");
                    return(false);
                }
            }
            if ((ShaderInfo.Stages & ShaderStages.Fragment) > 0)
            {
                try
                {
                    var path = Path.Combine(outDir, outName + ".frag");
                    if (File.Exists(path))
                    {
                        File.Delete(path);
                    }
                    File.WriteAllText(path, visitor.GLSL.GetGLSLOutput(ShaderStages.Fragment));
                }
                catch (Exception e)
                {
                    error = new CompileError(ErrorSource.Output, 0, 0,
                                             $"Unable to write fragment source file, reason: '{e.Message.Substring(0, e.Message.Length - 1)}'.");
                    return(false);
                }
            }

            error = null;
            return(true);
        }
예제 #7
0
        /// <summary>
        /// Performs the compilation of the input file, using the given options.
        /// </summary>
        /// <param name="options">The options to use when compiling the shader file.</param>
        /// <param name="error">The error generated by the compiler, if any. Will be <c>null</c> on success.</param>
        /// <returns><c>true</c> if the compilation completes successfully, <c>false</c> otherwise.</returns>
        public bool Compile(CompileOptions options, out CompileError error)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }
            error = null;

            // Validate the build options
            options.Validate();

            // Create the lexer and parser
            AntlrInputStream  inStream    = new AntlrInputStream(Source);
            SSLLexer          lexer       = new SSLLexer(inStream);
            CommonTokenStream tokenStream = new CommonTokenStream(lexer);
            SSLParser         parser      = new SSLParser(tokenStream);

            // Register our custom error listener
            lexer.RemoveErrorListeners();
            parser.RemoveErrorListeners();
            var err = new SSLErrorListener();

            lexer.AddErrorListener(err);
            parser.AddErrorListener(err);

            // Check for the version statement first
            var versionCtx = parser.versionMetaStatement();

            if (err.Error != null)
            {
                error = err.Error;
                return(false);
            }
            if (versionCtx != null && !checkVersion(versionCtx, out error))
            {
                return(false);
            }

            // Perform the parsing, and report the error if there is one
            parser.Reset();
            var fileCtx = parser.file();

            if (err.Error != null)
            {
                error = err.Error;
                return(false);
            }

            // Visit the tree (this step actually generates the GLSL)
            SSLVisitor visitor = new SSLVisitor(tokenStream, this, options);

            try
            {
                visitor.Visit(fileCtx);
                ShaderInfo = visitor.Info;
            }
            catch (VisitException e)
            {
                error = e.Error;
                return(false);
            }

            // Output the reflection if requested
            if (options.OutputReflection && !outputRefl(options, visitor, out error))
            {
                return(false);
            }

            // Output the GLSL if requested
            if (options.OutputGLSL && !outputGLSL(options, visitor, out error))
            {
                return(false);
            }

            // Compile if requested
            if (options.Compile && !compile(options, visitor, out error))
            {
                return(false);
            }

            return(true);
        }