/// <summary>Appends the default line terminator to the end of this instance.</summary> public void AppendLine() => _vsb.AppendLine();
private static void PreprocessShaderSource(AssetManager assetManager, ref Utf16ValueStringBuilder stringBuilder, string shaderPath, List <string> shaderIncludePaths, Stack <string> includeStack) { string shaderSource = GetShaderSource(assetManager, shaderPath); // Skip pre-processing if the string doesn't have any include directives. if (!shaderSource.Contains("#include ")) { stringBuilder.Append(shaderSource); return; } // Add current include path to the stack so we can book-keep to prevent circular dependencies. includeStack.Push(shaderPath); int lineNumber = 1; using var lineReader = new LineReader(shaderSource); for (ReadOnlyMemory <char> line = lineReader.ReadLine(); !lineReader.Finished; line = lineReader.ReadLine()) { // Check if the line starts with "#include " if (line.Span.StartsWith(_includeCompare.Span)) { if (TryParseIncludeLine(line, out var includeFile)) { string includePath = Path.Combine(Path.GetDirectoryName(shaderPath) ?? throw new Exception(), includeFile.ToString()); if (includeStack.Contains(includePath)) { throw new ShaderPreprocessException($"Include statement would introduce cyclic dependency: {includeFile}\n" + $"Parent: \"{shaderPath}\""); } shaderIncludePaths.Add(includePath); stringBuilder.Append("#line 1 "); stringBuilder.AppendLine((shaderIncludePaths.IndexOf(includePath) + 1).ToString()); PreprocessShaderSource(assetManager, ref stringBuilder, includePath, shaderIncludePaths, includeStack); stringBuilder.Append("\n#line "); stringBuilder.Append(lineNumber); stringBuilder.AppendLine((shaderIncludePaths.IndexOf(includePath) + 1).ToString()); } else { throw new ShaderPreprocessException($"Error when parsing include statement: {shaderPath}:{lineNumber}"); } } else { stringBuilder.Append(line); lineNumber++; } stringBuilder.Append('\n'); } includeStack.Pop(); }