private bool checkVersion(SSLParser.VersionMetaStatementContext ctx, out CompileError err) { var vstr = ctx.Version.Text.Split('.'); uint maj = 0, min = 0, bld = 0; if (!UInt32.TryParse(vstr[0], out maj) || !UInt32.TryParse(vstr[1], out min) || !UInt32.TryParse(vstr[2], out bld)) { err = new CompileError(ErrorSource.Parser, (uint)ctx.Start.Line, (uint)ctx.Start.StartIndex, $"Unable to parse version statement '{ctx.Version.Text}'."); return(false); } if (maj > 255 || min > 255 || bld > 255) { err = new CompileError(ErrorSource.Parser, (uint)ctx.Start.Line, (uint)ctx.Start.StartIndex, $"Version integer components must be <= 255 ({ctx.Version.Text})'."); return(false); } var version = new Version((int)maj, (int)min, (int)bld); if (!Array.Exists(VALID_SOURCE_VERSIONS, vv => vv == version)) { err = new CompileError(ErrorSource.Parser, (uint)ctx.Start.Line, (uint)ctx.Start.StartIndex, $"The targeted version for the source code ({version}) is not a valid version."); return(false); } if (version > TOOL_VERSION) { err = new CompileError(ErrorSource.Parser, (uint)ctx.Start.Line, (uint)ctx.Start.StartIndex, $"Version mismatch - requires version '{version}', but highest available is version '{TOOL_VERSION.Major}.{TOOL_VERSION.Minor}.{TOOL_VERSION.Build}'."); return(false); } err = null; return(true); }
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); }
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); }
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); }
public override void SyntaxError(TextWriter output, IRecognizer recognizer, IToken offendingSymbol, int line, int charPositionInLine, string msg, RecognitionException e) { var context = e?.Context; var ridx = context?.RuleIndex ?? -1; var badText = offendingSymbol?.Text ?? e.OffendingToken?.Text ?? ""; string errMsg = null; // TODO: Cause problems to see what errors come out, and fill this out with more intelligent error messages as we go if (ridx == SSLParser.RULE_file) { errMsg = $"Unexpected input '{badText}' at top level; expected type block, uniform, or function."; } else if (ridx == SSLParser.RULE_versionMetaStatement) { errMsg = $"Invalid shader version statement, ensure it is of the format 'version x.y.z;', where x, y, and z are all in [0, 255]."; } else if (ridx == SSLParser.RULE_uniformQualifier) { errMsg = $"Invalid or empty uniform qualifier."; } else if (ridx == SSLParser.RULE_imageLayoutQualifier) { errMsg = $"Invalid image format '{badText}'."; } else { if (msg.Contains("missing ';' at")) { errMsg = "Unexpected statement... are you missing a semicolon in the preceeding line?"; } else { errMsg = $"(Rule '{((ridx == -1) ? "none" : SSLParser.ruleNames[ridx])}') ('{badText}') - {msg}"; } } var stack = ((SSLParser)recognizer).GetRuleInvocationStack().ToList(); stack.Reverse(); Error = new CompileError(ErrorSource.Parser, (uint)line, (uint)charPositionInLine, errMsg, stack.ToArray()); }
// 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); }
public VisitException(CompileError error) { Error = error; }
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); }
/// <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); }